Skip to content

libct: simplify exec fifo wait using poll(2)#5271

Open
captainmo1 wants to merge 1 commit into
opencontainers:mainfrom
captainmo1:5251-simplify-exec-fifo-wait
Open

libct: simplify exec fifo wait using poll(2)#5271
captainmo1 wants to merge 1 commit into
opencontainers:mainfrom
captainmo1:5251-simplify-exec-fifo-wait

Conversation

@captainmo1

Copy link
Copy Markdown

Replace the goroutine + channel + 100ms time.After + blocking-open in handleFifo with a poll(2) loop on a non-blocking-open. Use pidfd_open(2) where available to wait for init exit without timeout, falling back to /proc state checks with 100ms timeout on older kernels. Allowing us to wait on both write to execfifo and state of init process simultaneously.

Fixes #5251

@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch 2 times, most recently from e91e67c to 1889c08 Compare May 11, 2026 20:37
// or the init process has exited. It does not consume the data — it only
// returns once a subsequent read on f will not block indefinitely.
func waitForFifoReady(f *os.File, pid int) error {
pidFd, err := unix.PidfdOpen(pid, 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use https://pkg.go.dev/os#Process.WithHandle here:

proc, _ := os.FindProcess(pid) // or add process() to parentProcess interface
var readyErr error
err := proc.WithHandle(func(pidfd uintptr) {
   readyErr = waitForReadyPidfd(f, int(pidfd))
})
if err != nil { // pidfd not supported
   return waitForFifoReadyPolling(f, pid)
}

This works better because WithHandle will only call the function if pidfd is fully supported by the kernel (there are many kernels with different nuances and you can rely on the Go runtime check).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree its much better

@captainmo1 captainmo1 May 14, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os.Process.WithHandle became available in Go 1.26 but runc's go.mod is currently at go 1.25.0. We'll need to bump the version or I can continue using raw unix.PidfOpen for now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a // TODO: switch to os.Process.WithHandle once go < 1.26 is no longer supported. or somesuch.

Comment thread libcontainer/container_linux.go

@kolyshkin kolyshkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Left a few comments, but overall this looks good.

@KhurshidMurotov

Copy link
Copy Markdown

this is nice one

@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch 3 times, most recently from 73c4cd4 to 460ea7f Compare May 19, 2026 15:28
@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch from 460ea7f to 72a9bd5 Compare May 22, 2026 22:29
@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch from 72a9bd5 to 79e2c6d Compare May 31, 2026 19:32

@lifubang lifubang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great refactoring work! LGTM overall, with a few minor nits.

Comment thread libcontainer/container_linux.go Outdated
// ENOSYS: kernel < 5.3, no pidfd_open at all.
// ESRCH: process is already gone — fall through to the polling path,
// which will detect the dead process on its first liveness check.
// Other errors (EINVAL, EMFILE, etc.) are unexpected; bail.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we need to deal with EMFILE, for example in dind.
So I think we should fall through to the polling path anyway once we got an error:

func waitForFifoReady(f *os.File, pid int) error {
	// TODO: switch to os.Process.WithHandle once go < 1.26 is no longer supported.
	pidFd, err := unix.PidfdOpen(pid, 0)
	if err == nil {
		defer unix.Close(pidFd)
		return waitForFifoReadyPidfd(f, pidFd)
	}

	// fall through to the polling path.
	return waitForFifoReadyPolling(f, pid)
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those errors wouldn't warrant a hard fail. got it

Comment thread libcontainer/container_linux.go Outdated
if err != nil {
return fmt.Errorf("poll exec fifo: %w", err)
}
if n > 0 && pfd[0].Revents&unix.POLLIN != 0 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add a POLLHUP check to reduce a system.Stat call in some situations:

if n > 0 && pfd[0].Revents&(unix.POLLIN|unix.POLLHUP) != 0 {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll update it.

@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch from 79e2c6d to 78c6c52 Compare June 2, 2026 14:11
Comment thread libcontainer/container_linux.go Outdated
if n > 0 && pfd[0].Revents&(unix.POLLIN|unix.POLLHUP) != 0 {
return nil
}
// If init is dead, fall

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The line break here splits "fall through", which reads awkwardly. Suggest merging this comment into a single line.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seen it. thanks

@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch from 78c6c52 to 2cc449c Compare June 2, 2026 15:10
lifubang
lifubang previously approved these changes Jun 2, 2026

@lifubang lifubang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

@captainmo1 captainmo1 force-pushed the 5251-simplify-exec-fifo-wait branch from 2cc449c to 7785938 Compare June 2, 2026 15:45
@lifubang lifubang dismissed their stale review June 3, 2026 01:01

Please avoid modifying unrelated lines in your last force push.

@lifubang lifubang force-pushed the 5251-simplify-exec-fifo-wait branch 2 times, most recently from ed376c2 to 02b8153 Compare June 4, 2026 15:15
Comment thread libcontainer/container_linux.go Outdated
@lifubang lifubang force-pushed the 5251-simplify-exec-fifo-wait branch from 02b8153 to 5f365bd Compare June 6, 2026 00:52
Replace the goroutine + channel + 100ms time.After + blocking open
in handleFifo with a poll(2) loop on a non-blocking open. Use
pidfd_open(2) where available to wait for init exit without timeout,
falling back to /proc state checks with 100ms timeout on older
kernels.

Fixes opencontainers#5251

Signed-off-by: Mohammed Aminu Futa <mohammedfuta2000@gmail.com>
Signed-off-by: lifubang <lifubang@acmcoder.com>
@lifubang lifubang force-pushed the 5251-simplify-exec-fifo-wait branch from 5f365bd to 937d887 Compare June 6, 2026 00:55
@lifubang

lifubang commented Jun 9, 2026

Copy link
Copy Markdown
Member

@opencontainers/runc-maintainers PTAL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

exec.fifo handling is way too complicated

4 participants