@@ -38,31 +38,10 @@ const (
38
38
type Process struct {
39
39
Pid int
40
40
41
- // State contains the atomic process state.
41
+ // state contains the atomic process state.
42
42
//
43
- // If handle is nil, this consists only of the processStatus fields,
43
+ // This consists of the processStatus fields,
44
44
// 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.
66
45
state atomic.Uint64
67
46
68
47
// Used only when handle is nil
@@ -151,7 +130,6 @@ func newHandleProcess(pid int, handle uintptr) *Process {
151
130
Pid : pid ,
152
131
handle : ph ,
153
132
}
154
- p .state .Store (1 ) // 1 persistent reference
155
133
runtime .SetFinalizer (p , (* Process ).Release )
156
134
return p
157
135
}
@@ -170,53 +148,31 @@ func (p *Process) handleTransientAcquire() (uintptr, processStatus) {
170
148
panic ("handleTransientAcquire called in invalid mode" )
171
149
}
172
150
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 {
186
157
return h , statusOK
187
158
}
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 )
188
169
}
189
170
190
171
func (p * Process ) handleTransientRelease () {
191
172
if p .handle == nil {
192
173
panic ("handleTransientRelease called in invalid mode" )
193
174
}
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 ()
220
176
}
221
177
222
178
// Drop the Process' persistent reference on the handle, deactivating future
@@ -230,8 +186,8 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
230
186
}
231
187
232
188
for {
233
- refs := p .state .Load ()
234
- status := processStatus (refs & processStatusMask )
189
+ state := p .state .Load ()
190
+ status := processStatus (state & processStatusMask )
235
191
if status != statusOK {
236
192
// Both Release and successful Wait will drop the
237
193
// Process' persistent reference on the handle. We
@@ -240,13 +196,7 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
240
196
// reference is dropped exactly once.
241
197
return status
242
198
}
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 )) {
250
200
continue
251
201
}
252
202
p .handle .release ()
0 commit comments