Skip to content

Commit caf29da

Browse files
ianlancetaylorgopherbot
authored andcommitted
os: don't store reference count in Process.state
We only need a reference count in processHandle. For #70907 Fixes #71564 Change-Id: I209ded869203dea10f12b070190774fb5f1d3d71 Reviewed-on: https://go-review.googlesource.com/c/go/+/638577 Commit-Queue: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
1 parent 637c235 commit caf29da

File tree

1 file changed

+22
-72
lines changed

1 file changed

+22
-72
lines changed

src/os/exec.go

Lines changed: 22 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,10 @@ const (
3838
type Process struct {
3939
Pid int
4040

41-
// State contains the atomic process state.
41+
// state contains the atomic process state.
4242
//
43-
// If handle is nil, this consists only of the processStatus fields,
43+
// This consists of the processStatus fields,
4444
// which indicate if the process is done/released.
45-
//
46-
// In handle is not nil, the lower bits also contain a reference
47-
// count for the handle field.
48-
//
49-
// The Process itself initially holds 1 persistent reference. Any
50-
// operation that uses the handle with a system call temporarily holds
51-
// an additional transient reference. This prevents the handle from
52-
// being closed prematurely, which could result in the OS allocating a
53-
// different handle with the same value, leading to Process' methods
54-
// operating on the wrong process.
55-
//
56-
// Release and Wait both drop the Process' persistent reference, but
57-
// other concurrent references may delay actually closing the handle
58-
// because they hold a transient reference.
59-
//
60-
// Regardless, we want new method calls to immediately treat the handle
61-
// as unavailable after Release or Wait to avoid extending this delay.
62-
// This is achieved by setting either processStatus flag when the
63-
// Process' persistent reference is dropped. The only difference in the
64-
// flags is the reason the handle is unavailable, which affects the
65-
// errors returned by concurrent calls.
6645
state atomic.Uint64
6746

6847
// Used only when handle is nil
@@ -151,7 +130,6 @@ func newHandleProcess(pid int, handle uintptr) *Process {
151130
Pid: pid,
152131
handle: ph,
153132
}
154-
p.state.Store(1) // 1 persistent reference
155133
runtime.SetFinalizer(p, (*Process).Release)
156134
return p
157135
}
@@ -170,53 +148,31 @@ func (p *Process) handleTransientAcquire() (uintptr, processStatus) {
170148
panic("handleTransientAcquire called in invalid mode")
171149
}
172150

173-
for {
174-
refs := p.state.Load()
175-
if refs&processStatusMask != 0 {
176-
return 0, processStatus(refs & processStatusMask)
177-
}
178-
new := refs + 1
179-
if !p.state.CompareAndSwap(refs, new) {
180-
continue
181-
}
182-
h, ok := p.handle.acquire()
183-
if !ok {
184-
panic("inconsistent reference counts")
185-
}
151+
state := p.state.Load()
152+
if state&processStatusMask != 0 {
153+
return 0, processStatus(state & processStatusMask)
154+
}
155+
h, ok := p.handle.acquire()
156+
if ok {
186157
return h, statusOK
187158
}
159+
160+
// This case means that the handle has been closed.
161+
// We always set the status to non-zero before closing the handle.
162+
// If we get here the status must have been set non-zero after
163+
// we just checked it above.
164+
state = p.state.Load()
165+
if state&processStatusMask == 0 {
166+
panic("inconsistent process status")
167+
}
168+
return 0, processStatus(state & processStatusMask)
188169
}
189170

190171
func (p *Process) handleTransientRelease() {
191172
if p.handle == nil {
192173
panic("handleTransientRelease called in invalid mode")
193174
}
194-
195-
for {
196-
state := p.state.Load()
197-
refs := state &^ processStatusMask
198-
status := processStatus(state & processStatusMask)
199-
if refs == 0 {
200-
// This should never happen because
201-
// handleTransientRelease is always paired with
202-
// handleTransientAcquire.
203-
panic("release of handle with refcount 0")
204-
}
205-
if refs == 1 && status == statusOK {
206-
// Process holds a persistent reference and always sets
207-
// a status when releasing that reference
208-
// (handlePersistentRelease). Thus something has gone
209-
// wrong if this is the last release but a status has
210-
// not always been set.
211-
panic("final release of handle without processStatus")
212-
}
213-
new := state - 1
214-
if !p.state.CompareAndSwap(state, new) {
215-
continue
216-
}
217-
p.handle.release()
218-
return
219-
}
175+
p.handle.release()
220176
}
221177

222178
// Drop the Process' persistent reference on the handle, deactivating future
@@ -230,8 +186,8 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
230186
}
231187

232188
for {
233-
refs := p.state.Load()
234-
status := processStatus(refs & processStatusMask)
189+
state := p.state.Load()
190+
status := processStatus(state & processStatusMask)
235191
if status != statusOK {
236192
// Both Release and successful Wait will drop the
237193
// Process' persistent reference on the handle. We
@@ -240,13 +196,7 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
240196
// reference is dropped exactly once.
241197
return status
242198
}
243-
if refs == 0 {
244-
// This should never happen because dropping the
245-
// persistent reference always sets a status.
246-
panic("release of handle with refcount 0")
247-
}
248-
new := (refs - 1) | uint64(reason)
249-
if !p.state.CompareAndSwap(refs, new) {
199+
if !p.state.CompareAndSwap(state, uint64(reason)) {
250200
continue
251201
}
252202
p.handle.release()

0 commit comments

Comments
 (0)