container/init: use one channel for wait4
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m20s
Test / Hakurei (push) Successful in 3m12s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m6s
Test / Hakurei (race detector) (push) Successful in 4m51s
Test / Flake checks (push) Successful in 1m31s

When using two channels it is possible for the other case to be reached before all pending winfo are consumed, causing incorrect reporting.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-10-15 21:35:19 +09:00
parent 52e3324ef4
commit ae65491223
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
2 changed files with 25 additions and 7 deletions

View File

@ -353,10 +353,14 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
wpid int
wstatus WaitStatus
}
// info is closed as the wait4 thread terminates
// when there are no longer any processes left to reap
info := make(chan winfo, 1)
done := make(chan struct{})
k.new(func(k syscallDispatcher) {
k.lockOSThread()
var (
err error
wpid = -2
@ -382,7 +386,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
k.printf(msg, "unexpected wait4 response: %v", err)
}
close(done)
close(info)
})
// handle signals to dump withheld messages
@ -411,7 +415,13 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
msg.BeforeExit()
k.exit(0)
case w := <-info:
case w, ok := <-info:
if !ok {
msg.BeforeExit()
k.exit(r)
continue // unreachable
}
if w.wpid == cmd.Process.Pid {
// initial process exited, output is most likely available again
msg.Resume()
@ -433,10 +443,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }()
}
case <-done:
msg.BeforeExit()
k.exit(r)
case <-timeout:
k.printf(msg, "timeout exceeded waiting for lingering processes")
msg.BeforeExit()

View File

@ -2081,6 +2081,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
// magicWait4Signal as args[4] causes this to block until simulated signal is delivered
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil, magicWait4Signal}, 0xbad, nil),
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
@ -2174,6 +2176,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, stub.PanicExit}, 0, syscall.ECHILD),
}}},
@ -2266,6 +2270,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil}, 0xbad, nil),
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, 0xdeadbeef}, 0, syscall.ECHILD),
@ -2358,6 +2364,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xdeaf), 0, nil}, 0xbabe, nil),
@ -2494,6 +2502,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xdeaf), 0, nil}, 0xbabe, nil),
@ -2634,6 +2644,8 @@ func TestInitEntrypoint(t *testing.T) {
/* wait4 */
Tracks: []stub.Expect{{Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xdeaf), 0, nil}, 0xbabe, nil),