mirror of
https://github.com/golang/go
synced 2024-11-19 16:44:43 -07:00
cea29c4a35
On systems that support the POSIX.1-2008 waitid function, we can use it to block until a wait will succeed. This avoids a possible race condition: if a program calls p.Kill/p.Signal and p.Wait from two different goroutines, then it is possible for the wait to complete just before the signal is sent. In that case, it is possible that the system will start a new process using the same PID between the wait and the signal, causing the signal to be sent to the wrong process. The Process.isdone field attempts to avoid that race, but there is a small gap of time between when wait returns and isdone is set when the race can occur. This CL avoids that race by using waitid to wait until the process has exited without actually collecting the PID. Then it sets isdone, then waits for any active signals to complete, and only then collects the PID. No test because any plausible test would require starting enough processes to recycle all the process IDs. Update #13987. Update #16028. Change-Id: Id2939431991d3b355dfb22f08793585fc0568ce8 Reviewed-on: https://go-review.googlesource.com/23967 Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
100 lines
2.1 KiB
Go
100 lines
2.1 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
|
|
|
package os
|
|
|
|
import (
|
|
"errors"
|
|
"runtime"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func (p *Process) wait() (ps *ProcessState, err error) {
|
|
if p.Pid == -1 {
|
|
return nil, syscall.EINVAL
|
|
}
|
|
|
|
// If we can block until Wait4 will succeed immediately, do so.
|
|
ready, err := p.blockUntilWaitable()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ready {
|
|
// Mark the process done now, before the call to Wait4,
|
|
// so that Process.signal will not send a signal.
|
|
p.setDone()
|
|
// Acquire a write lock on sigMu to wait for any
|
|
// active call to the signal method to complete.
|
|
p.sigMu.Lock()
|
|
p.sigMu.Unlock()
|
|
}
|
|
|
|
var status syscall.WaitStatus
|
|
var rusage syscall.Rusage
|
|
pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
|
|
if e != nil {
|
|
return nil, NewSyscallError("wait", e)
|
|
}
|
|
if pid1 != 0 {
|
|
p.setDone()
|
|
}
|
|
ps = &ProcessState{
|
|
pid: pid1,
|
|
status: status,
|
|
rusage: &rusage,
|
|
}
|
|
return ps, nil
|
|
}
|
|
|
|
var errFinished = errors.New("os: process already finished")
|
|
|
|
func (p *Process) signal(sig Signal) error {
|
|
if p.Pid == -1 {
|
|
return errors.New("os: process already released")
|
|
}
|
|
if p.Pid == 0 {
|
|
return errors.New("os: process not initialized")
|
|
}
|
|
p.sigMu.RLock()
|
|
defer p.sigMu.RUnlock()
|
|
if p.done() {
|
|
return errFinished
|
|
}
|
|
s, ok := sig.(syscall.Signal)
|
|
if !ok {
|
|
return errors.New("os: unsupported signal type")
|
|
}
|
|
if e := syscall.Kill(p.Pid, s); e != nil {
|
|
if e == syscall.ESRCH {
|
|
return errFinished
|
|
}
|
|
return e
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Process) release() error {
|
|
// NOOP for unix.
|
|
p.Pid = -1
|
|
// no need for a finalizer anymore
|
|
runtime.SetFinalizer(p, nil)
|
|
return nil
|
|
}
|
|
|
|
func findProcess(pid int) (p *Process, err error) {
|
|
// NOOP for unix.
|
|
return newProcess(pid, 0), nil
|
|
}
|
|
|
|
func (p *ProcessState) userTime() time.Duration {
|
|
return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
|
|
}
|
|
|
|
func (p *ProcessState) systemTime() time.Duration {
|
|
return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
|
|
}
|