fix: enforce durable Windows launch release barrier
This commit is contained in:
@@ -95,13 +95,16 @@ type execProcess struct {
|
||||
started time.Time
|
||||
waitFn func() (int32, bool, error)
|
||||
killFn func(uint32) error
|
||||
releaseFn func() error
|
||||
snapshotFn func() (supervisor.ResourceSnapshot, error)
|
||||
|
||||
mu sync.Mutex
|
||||
finished bool
|
||||
status supervisor.ExitStatus
|
||||
waitErr error
|
||||
closeIn sync.Once
|
||||
mu sync.Mutex
|
||||
finished bool
|
||||
status supervisor.ExitStatus
|
||||
waitErr error
|
||||
closeIn sync.Once
|
||||
release sync.Once
|
||||
releaseErr error
|
||||
}
|
||||
|
||||
type outputResult struct {
|
||||
@@ -113,6 +116,21 @@ func (process *execProcess) IssueUUID() domain.UUID { return process.issue }
|
||||
|
||||
func (process *execProcess) Identity() supervisor.EffectiveIdentity { return process.identity }
|
||||
|
||||
// Release is the second half of the durable launch barrier. The portable
|
||||
// adapter has no suspended native handle, so its release is intentionally a
|
||||
// no-op; the Windows adapter supplies a ResumeThread closure.
|
||||
func (process *execProcess) Release(_ context.Context) error {
|
||||
if process == nil {
|
||||
return ErrProcessNotReady
|
||||
}
|
||||
process.release.Do(func() {
|
||||
if process.releaseFn != nil {
|
||||
process.releaseErr = process.releaseFn()
|
||||
}
|
||||
})
|
||||
return process.releaseErr
|
||||
}
|
||||
|
||||
func (process *execProcess) ReadOutput(ctx context.Context) (supervisor.OutputChunk, error) {
|
||||
if process == nil || process.outputs == nil {
|
||||
return supervisor.OutputChunk{}, ErrProcessNotReady
|
||||
@@ -453,15 +471,15 @@ func (manager *execSupervisor) startCommand(ctx context.Context, spec supervisor
|
||||
}
|
||||
return code, false, err
|
||||
}
|
||||
return manager.registerProcess(spec.IssueUUID, identity, command, stdin, stdout, stderr, started, waitFn, func(uint32) error { return command.Process.Kill() }, cleanup), nil
|
||||
return manager.registerProcess(spec.IssueUUID, identity, command, stdin, stdout, stderr, started, waitFn, func(uint32) error { return command.Process.Kill() }, nil, cleanup), nil
|
||||
}
|
||||
|
||||
func (manager *execSupervisor) registerProcess(issue domain.UUID, identity supervisor.EffectiveIdentity, command *exec.Cmd, stdin io.WriteCloser, stdout, stderr io.ReadCloser, started time.Time, waitFn func() (int32, bool, error), killFn func(uint32) error, cleanup func()) *execProcess {
|
||||
func (manager *execSupervisor) registerProcess(issue domain.UUID, identity supervisor.EffectiveIdentity, command *exec.Cmd, stdin io.WriteCloser, stdout, stderr io.ReadCloser, started time.Time, waitFn func() (int32, bool, error), killFn func(uint32) error, releaseFn func() error, cleanup func()) *execProcess {
|
||||
var pid uint32
|
||||
if command != nil && command.Process != nil && command.Process.Pid > 0 {
|
||||
pid = uint32(command.Process.Pid)
|
||||
}
|
||||
process := &execProcess{issue: issue, identity: identity, cmd: command, pid: pid, stdin: stdin, stdout: stdout, stderr: stderr, outputs: make(chan outputResult, 32), done: make(chan struct{}), started: started, waitFn: waitFn, killFn: killFn}
|
||||
process := &execProcess{issue: issue, identity: identity, cmd: command, pid: pid, stdin: stdin, stdout: stdout, stderr: stderr, outputs: make(chan outputResult, 32), done: make(chan struct{}), started: started, waitFn: waitFn, killFn: killFn, releaseFn: releaseFn}
|
||||
manager.mu.Lock()
|
||||
manager.active[issue] = process
|
||||
manager.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user