| // 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. |
| |
| package os |
| |
| import ( |
| "errors" |
| "internal/testlog" |
| "runtime" |
| "sync" |
| "sync/atomic" |
| "syscall" |
| "time" |
| ) |
| |
| // ErrProcessDone indicates a [Process] has finished. |
| var ErrProcessDone = errors.New("os: process already finished") |
| |
| type processMode uint8 |
| |
| const ( |
| // modePID means that Process operations such use the raw PID from the |
| // Pid field. handle is not used. |
| // |
| // This may be due to the host not supporting handles, or because |
| // Process was created as a literal, leaving handle unset. |
| // |
| // This must be the zero value so Process literals get modePID. |
| modePID processMode = iota |
| |
| // modeHandle means that Process operations use handle, which is |
| // initialized with an OS process handle. |
| // |
| // Note that Release and Wait will deactivate and eventually close the |
| // handle, so acquire may fail, indicating the reason. |
| modeHandle |
| ) |
| |
| type processStatus uint64 |
| |
| const ( |
| // PID/handle OK to use. |
| statusOK processStatus = 0 |
| |
| // statusDone indicates that the PID/handle should not be used because |
| // the process is done (has been successfully Wait'd on). |
| statusDone processStatus = 1 << 62 |
| |
| // statusReleased indicates that the PID/handle should not be used |
| // because the process is released. |
| statusReleased processStatus = 1 << 63 |
| |
| processStatusMask = 0x3 << 62 |
| ) |
| |
| // Process stores the information about a process created by [StartProcess]. |
| type Process struct { |
| Pid int |
| |
| mode processMode |
| |
| // State contains the atomic process state. |
| // |
| // In modePID, this consists only of the processStatus fields, which |
| // indicate if the process is done/released. |
| // |
| // In modeHandle, the lower bits also contain a reference count for the |
| // handle field. |
| // |
| // The Process itself initially holds 1 persistent reference. Any |
| // operation that uses the handle with a system call temporarily holds |
| // an additional transient reference. This prevents the handle from |
| // being closed prematurely, which could result in the OS allocating a |
| // different handle with the same value, leading to Process' methods |
| // operating on the wrong process. |
| // |
| // Release and Wait both drop the Process' persistent reference, but |
| // other concurrent references may delay actually closing the handle |
| // because they hold a transient reference. |
| // |
| // Regardless, we want new method calls to immediately treat the handle |
| // as unavailable after Release or Wait to avoid extending this delay. |
| // This is achieved by setting either processStatus flag when the |
| // Process' persistent reference is dropped. The only difference in the |
| // flags is the reason the handle is unavailable, which affects the |
| // errors returned by concurrent calls. |
| state atomic.Uint64 |
| |
| // Used only in modePID. |
| sigMu sync.RWMutex // avoid race between wait and signal |
| |
| // handle is the OS handle for process actions, used only in |
| // modeHandle. |
| // |
| // handle must be accessed only via the handleTransientAcquire method |
| // (or during closeHandle), not directly! handle is immutable. |
| // |
| // On Windows, it is a handle from OpenProcess. |
| // On Linux, it is a pidfd. |
| // It is unused on other GOOSes. |
| handle uintptr |
| } |
| |
| func newPIDProcess(pid int) *Process { |
| p := &Process{ |
| Pid: pid, |
| mode: modePID, |
| } |
| runtime.SetFinalizer(p, (*Process).Release) |
| return p |
| } |
| |
| func newHandleProcess(pid int, handle uintptr) *Process { |
| p := &Process{ |
| Pid: pid, |
| mode: modeHandle, |
| handle: handle, |
| } |
| p.state.Store(1) // 1 persistent reference |
| runtime.SetFinalizer(p, (*Process).Release) |
| return p |
| } |
| |
| func newDoneProcess(pid int) *Process { |
| p := &Process{ |
| Pid: pid, |
| mode: modeHandle, |
| // N.B Since we set statusDone, handle will never actually be |
| // used, so its value doesn't matter. |
| } |
| p.state.Store(uint64(statusDone)) // No persistent reference, as there is no handle. |
| runtime.SetFinalizer(p, (*Process).Release) |
| return p |
| } |
| |
| func (p *Process) handleTransientAcquire() (uintptr, processStatus) { |
| if p.mode != modeHandle { |
| panic("handleTransientAcquire called in invalid mode") |
| } |
| |
| for { |
| refs := p.state.Load() |
| if refs&processStatusMask != 0 { |
| return 0, processStatus(refs & processStatusMask) |
| } |
| new := refs + 1 |
| if !p.state.CompareAndSwap(refs, new) { |
| continue |
| } |
| return p.handle, statusOK |
| } |
| } |
| |
| func (p *Process) handleTransientRelease() { |
| if p.mode != modeHandle { |
| panic("handleTransientRelease called in invalid mode") |
| } |
| |
| for { |
| state := p.state.Load() |
| refs := state &^ processStatusMask |
| status := processStatus(state & processStatusMask) |
| if refs == 0 { |
| // This should never happen because |
| // handleTransientRelease is always paired with |
| // handleTransientAcquire. |
| panic("release of handle with refcount 0") |
| } |
| if refs == 1 && status == statusOK { |
| // Process holds a persistent reference and always sets |
| // a status when releasing that reference |
| // (handlePersistentRelease). Thus something has gone |
| // wrong if this is the last release but a status has |
| // not always been set. |
| panic("final release of handle without processStatus") |
| } |
| new := state - 1 |
| if !p.state.CompareAndSwap(state, new) { |
| continue |
| } |
| if new&^processStatusMask == 0 { |
| p.closeHandle() |
| } |
| return |
| } |
| } |
| |
| // Drop the Process' persistent reference on the handle, deactivating future |
| // Wait/Signal calls with the passed reason. |
| // |
| // Returns the status prior to this call. If this is not statusOK, then the |
| // reference was not dropped or status changed. |
| func (p *Process) handlePersistentRelease(reason processStatus) processStatus { |
| if p.mode != modeHandle { |
| panic("handlePersistentRelease called in invalid mode") |
| } |
| |
| for { |
| refs := p.state.Load() |
| status := processStatus(refs & processStatusMask) |
| if status != statusOK { |
| // Both Release and successful Wait will drop the |
| // Process' persistent reference on the handle. We |
| // can't allow concurrent calls to drop the reference |
| // twice, so we use the status as a guard to ensure the |
| // reference is dropped exactly once. |
| return status |
| } |
| if refs == 0 { |
| // This should never happen because dropping the |
| // persistent reference always sets a status. |
| panic("release of handle with refcount 0") |
| } |
| new := (refs - 1) | uint64(reason) |
| if !p.state.CompareAndSwap(refs, new) { |
| continue |
| } |
| if new&^processStatusMask == 0 { |
| p.closeHandle() |
| } |
| return status |
| } |
| } |
| |
| func (p *Process) pidStatus() processStatus { |
| if p.mode != modePID { |
| panic("pidStatus called in invalid mode") |
| } |
| |
| return processStatus(p.state.Load()) |
| } |
| |
| func (p *Process) pidDeactivate(reason processStatus) { |
| if p.mode != modePID { |
| panic("pidDeactivate called in invalid mode") |
| } |
| |
| // Both Release and successful Wait will deactivate the PID. Only one |
| // of those should win, so nothing left to do here if the compare |
| // fails. |
| // |
| // N.B. This means that results can be inconsistent. e.g., with a |
| // racing Release and Wait, Wait may successfully wait on the process, |
| // returning the wait status, while future calls error with "process |
| // released" rather than "process done". |
| p.state.CompareAndSwap(0, uint64(reason)) |
| } |
| |
| // ProcAttr holds the attributes that will be applied to a new process |
| // started by StartProcess. |
| type ProcAttr struct { |
| // If Dir is non-empty, the child changes into the directory before |
| // creating the process. |
| Dir string |
| // If Env is non-nil, it gives the environment variables for the |
| // new process in the form returned by Environ. |
| // If it is nil, the result of Environ will be used. |
| Env []string |
| // Files specifies the open files inherited by the new process. The |
| // first three entries correspond to standard input, standard output, and |
| // standard error. An implementation may support additional entries, |
| // depending on the underlying operating system. A nil entry corresponds |
| // to that file being closed when the process starts. |
| // On Unix systems, StartProcess will change these File values |
| // to blocking mode, which means that SetDeadline will stop working |
| // and calling Close will not interrupt a Read or Write. |
| Files []*File |
| |
| // Operating system-specific process creation attributes. |
| // Note that setting this field means that your program |
| // may not execute properly or even compile on some |
| // operating systems. |
| Sys *syscall.SysProcAttr |
| } |
| |
| // A Signal represents an operating system signal. |
| // The usual underlying implementation is operating system-dependent: |
| // on Unix it is syscall.Signal. |
| type Signal interface { |
| String() string |
| Signal() // to distinguish from other Stringers |
| } |
| |
| // Getpid returns the process id of the caller. |
| func Getpid() int { return syscall.Getpid() } |
| |
| // Getppid returns the process id of the caller's parent. |
| func Getppid() int { return syscall.Getppid() } |
| |
| // FindProcess looks for a running process by its pid. |
| // |
| // The [Process] it returns can be used to obtain information |
| // about the underlying operating system process. |
| // |
| // On Unix systems, FindProcess always succeeds and returns a Process |
| // for the given pid, regardless of whether the process exists. To test whether |
| // the process actually exists, see whether p.Signal(syscall.Signal(0)) reports |
| // an error. |
| func FindProcess(pid int) (*Process, error) { |
| return findProcess(pid) |
| } |
| |
| // StartProcess starts a new process with the program, arguments and attributes |
| // specified by name, argv and attr. The argv slice will become [os.Args] in the |
| // new process, so it normally starts with the program name. |
| // |
| // If the calling goroutine has locked the operating system thread |
| // with [runtime.LockOSThread] and modified any inheritable OS-level |
| // thread state (for example, Linux or Plan 9 name spaces), the new |
| // process will inherit the caller's thread state. |
| // |
| // StartProcess is a low-level interface. The [os/exec] package provides |
| // higher-level interfaces. |
| // |
| // If there is an error, it will be of type [*PathError]. |
| func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) { |
| testlog.Open(name) |
| return startProcess(name, argv, attr) |
| } |
| |
| // Release releases any resources associated with the [Process] p, |
| // rendering it unusable in the future. |
| // Release only needs to be called if [Process.Wait] is not. |
| func (p *Process) Release() error { |
| // Note to future authors: the Release API is cursed. |
| // |
| // On Unix and Plan 9, Release sets p.Pid = -1. This is the only part of the |
| // Process API that is not thread-safe, but it can't be changed now. |
| // |
| // On Windows, Release does _not_ modify p.Pid. |
| // |
| // On Windows, Wait calls Release after successfully waiting to |
| // proactively clean up resources. |
| // |
| // On Unix and Plan 9, Wait also proactively cleans up resources, but |
| // can not call Release, as Wait does not set p.Pid = -1. |
| // |
| // On Unix and Plan 9, calling Release a second time has no effect. |
| // |
| // On Windows, calling Release a second time returns EINVAL. |
| return p.release() |
| } |
| |
| // Kill causes the [Process] to exit immediately. Kill does not wait until |
| // the Process has actually exited. This only kills the Process itself, |
| // not any other processes it may have started. |
| func (p *Process) Kill() error { |
| return p.kill() |
| } |
| |
| // Wait waits for the [Process] to exit, and then returns a |
| // ProcessState describing its status and an error, if any. |
| // Wait releases any resources associated with the Process. |
| // On most operating systems, the Process must be a child |
| // of the current process or an error will be returned. |
| func (p *Process) Wait() (*ProcessState, error) { |
| return p.wait() |
| } |
| |
| // Signal sends a signal to the [Process]. |
| // Sending [Interrupt] on Windows is not implemented. |
| func (p *Process) Signal(sig Signal) error { |
| return p.signal(sig) |
| } |
| |
| // UserTime returns the user CPU time of the exited process and its children. |
| func (p *ProcessState) UserTime() time.Duration { |
| return p.userTime() |
| } |
| |
| // SystemTime returns the system CPU time of the exited process and its children. |
| func (p *ProcessState) SystemTime() time.Duration { |
| return p.systemTime() |
| } |
| |
| // Exited reports whether the program has exited. |
| // On Unix systems this reports true if the program exited due to calling exit, |
| // but false if the program terminated due to a signal. |
| func (p *ProcessState) Exited() bool { |
| return p.exited() |
| } |
| |
| // Success reports whether the program exited successfully, |
| // such as with exit status 0 on Unix. |
| func (p *ProcessState) Success() bool { |
| return p.success() |
| } |
| |
| // Sys returns system-dependent exit information about |
| // the process. Convert it to the appropriate underlying |
| // type, such as [syscall.WaitStatus] on Unix, to access its contents. |
| func (p *ProcessState) Sys() any { |
| return p.sys() |
| } |
| |
| // SysUsage returns system-dependent resource usage information about |
| // the exited process. Convert it to the appropriate underlying |
| // type, such as [*syscall.Rusage] on Unix, to access its contents. |
| // (On Unix, *syscall.Rusage matches struct rusage as defined in the |
| // getrusage(2) manual page.) |
| func (p *ProcessState) SysUsage() any { |
| return p.sysUsage() |
| } |