Skip to content

Commit 5c2b5e0

Browse files
ianlancetaylorgopherbot
authored andcommitted
os: separate Process.handle into a separate memory allocation
This is a step toward using AddCleanup rather than SetFinalizer to close process handles. For #70907 Change-Id: I7fb37461dd67b27135eab46fbdae94f0058ace85 Reviewed-on: https://go-review.googlesource.com/c/go/+/638575 Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 0825475 commit 5c2b5e0

File tree

4 files changed

+82
-24
lines changed

4 files changed

+82
-24
lines changed

src/os/exec.go

+75-19
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,66 @@ type Process struct {
9090
// Used only in modePID.
9191
sigMu sync.RWMutex // avoid race between wait and signal
9292

93-
// handle is the OS handle for process actions, used only in
94-
// modeHandle.
95-
//
96-
// handle must be accessed only via the handleTransientAcquire method
97-
// (or during closeHandle), not directly! handle is immutable.
93+
// handle, if not nil, is a pointer to a struct
94+
// that holds the OS-specific process handle.
95+
// This pointer is set when Process is created,
96+
// and never changed afterward.
97+
// This is a pointer to a separate memory allocation
98+
// so that we can use runtime.AddCleanup.
99+
handle *processHandle
100+
}
101+
102+
// processHandle holds an operating system handle to a process.
103+
// This is only used on systems that support that concept,
104+
// currently Linux and Windows.
105+
// This maintains a reference count to the handle,
106+
// and closes the handle when the reference drops to zero.
107+
type processHandle struct {
108+
// The actual handle. This field should not be used directly.
109+
// Instead, use the acquire and release methods.
98110
//
99-
// On Windows, it is a handle from OpenProcess.
100-
// On Linux, it is a pidfd.
101-
// It is unused on other GOOSes.
111+
// On Windows this is a handle returned by OpenProcess.
112+
// On Linux this is a pidfd.
102113
handle uintptr
114+
115+
// Number of active references. When this drops to zero
116+
// the handle is closed.
117+
refs atomic.Int32
118+
}
119+
120+
// acquire adds a reference and returns the handle.
121+
// The bool result reports whether acquire succeeded;
122+
// it fails if the handle is already closed.
123+
// Every successful call to acquire should be paired with a call to release.
124+
func (ph *processHandle) acquire() (uintptr, bool) {
125+
for {
126+
refs := ph.refs.Load()
127+
if refs < 0 {
128+
panic("internal error: negative process handle reference count")
129+
}
130+
if refs == 0 {
131+
return 0, false
132+
}
133+
if ph.refs.CompareAndSwap(refs, refs+1) {
134+
return ph.handle, true
135+
}
136+
}
137+
}
138+
139+
// release releases a reference to the handle.
140+
func (ph *processHandle) release() {
141+
for {
142+
refs := ph.refs.Load()
143+
if refs <= 0 {
144+
panic("internal error: too many releases of process handle")
145+
}
146+
if ph.refs.CompareAndSwap(refs, refs-1) {
147+
if refs == 1 {
148+
ph.closeHandle()
149+
}
150+
return
151+
}
152+
}
103153
}
104154

105155
func newPIDProcess(pid int) *Process {
@@ -112,10 +162,18 @@ func newPIDProcess(pid int) *Process {
112162
}
113163

114164
func newHandleProcess(pid int, handle uintptr) *Process {
165+
ph := &processHandle{
166+
handle: handle,
167+
}
168+
169+
// Start the reference count as 1,
170+
// meaning the reference from the returned Process.
171+
ph.refs.Store(1)
172+
115173
p := &Process{
116174
Pid: pid,
117175
mode: modeHandle,
118-
handle: handle,
176+
handle: ph,
119177
}
120178
p.state.Store(1) // 1 persistent reference
121179
runtime.SetFinalizer(p, (*Process).Release)
@@ -125,9 +183,7 @@ func newHandleProcess(pid int, handle uintptr) *Process {
125183
func newDoneProcess(pid int) *Process {
126184
p := &Process{
127185
Pid: pid,
128-
mode: modeHandle,
129-
// N.B Since we set statusDone, handle will never actually be
130-
// used, so its value doesn't matter.
186+
mode: modePID,
131187
}
132188
p.state.Store(uint64(statusDone)) // No persistent reference, as there is no handle.
133189
runtime.SetFinalizer(p, (*Process).Release)
@@ -148,7 +204,11 @@ func (p *Process) handleTransientAcquire() (uintptr, processStatus) {
148204
if !p.state.CompareAndSwap(refs, new) {
149205
continue
150206
}
151-
return p.handle, statusOK
207+
h, ok := p.handle.acquire()
208+
if !ok {
209+
panic("inconsistent reference counts")
210+
}
211+
return h, statusOK
152212
}
153213
}
154214

@@ -179,9 +239,7 @@ func (p *Process) handleTransientRelease() {
179239
if !p.state.CompareAndSwap(state, new) {
180240
continue
181241
}
182-
if new&^processStatusMask == 0 {
183-
p.closeHandle()
184-
}
242+
p.handle.release()
185243
return
186244
}
187245
}
@@ -216,9 +274,7 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
216274
if !p.state.CompareAndSwap(refs, new) {
217275
continue
218276
}
219-
if new&^processStatusMask == 0 {
220-
p.closeHandle()
221-
}
277+
p.handle.release()
222278
return status
223279
}
224280
}

src/os/exec_linux.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ import (
88
"syscall"
99
)
1010

11-
func (p *Process) closeHandle() {
12-
syscall.Close(int(p.handle))
11+
func (ph *processHandle) closeHandle() {
12+
syscall.Close(int(ph.handle))
1313
}

src/os/exec_nohandle.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66

77
package os
88

9-
func (p *Process) closeHandle() {}
9+
func (ph *processHandle) closeHandle() {
10+
panic("internal error: unexpected call to closeHandle")
11+
}

src/os/exec_windows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ func (p *Process) release() error {
8888
return nil
8989
}
9090

91-
func (p *Process) closeHandle() {
92-
syscall.CloseHandle(syscall.Handle(p.handle))
91+
func (ph *processHandle) closeHandle() {
92+
syscall.CloseHandle(syscall.Handle(ph.handle))
9393
}
9494

9595
func findProcess(pid int) (p *Process, err error) {

0 commit comments

Comments
 (0)