@@ -176,40 +176,6 @@ func (p *Process) handleTransientRelease() {
176
176
p .handle .release ()
177
177
}
178
178
179
- // Drop the Process' persistent reference on the handle, deactivating future
180
- // Wait/Signal calls with the passed reason.
181
- //
182
- // Returns the status prior to this call. If this is not statusOK, then the
183
- // reference was not dropped or status changed.
184
- func (p * Process ) handlePersistentRelease (reason processStatus ) processStatus {
185
- if p .handle == nil {
186
- panic ("handlePersistentRelease called in invalid mode" )
187
- }
188
-
189
- for {
190
- state := p .state .Load ()
191
- status := processStatus (state )
192
- if status != statusOK {
193
- // Both Release and successful Wait will drop the
194
- // Process' persistent reference on the handle. We
195
- // can't allow concurrent calls to drop the reference
196
- // twice, so we use the status as a guard to ensure the
197
- // reference is dropped exactly once.
198
- return status
199
- }
200
- if ! p .state .CompareAndSwap (state , uint32 (reason )) {
201
- continue
202
- }
203
-
204
- // No need for more cleanup.
205
- p .cleanup .Stop ()
206
-
207
- p .handle .release ()
208
-
209
- return status
210
- }
211
- }
212
-
213
179
func (p * Process ) pidStatus () processStatus {
214
180
if p .handle != nil {
215
181
panic ("pidStatus called in invalid mode" )
@@ -218,22 +184,6 @@ func (p *Process) pidStatus() processStatus {
218
184
return processStatus (p .state .Load ())
219
185
}
220
186
221
- func (p * Process ) pidDeactivate (reason processStatus ) {
222
- if p .handle != nil {
223
- panic ("pidDeactivate called in invalid mode" )
224
- }
225
-
226
- // Both Release and successful Wait will deactivate the PID. Only one
227
- // of those should win, so nothing left to do here if the compare
228
- // fails.
229
- //
230
- // N.B. This means that results can be inconsistent. e.g., with a
231
- // racing Release and Wait, Wait may successfully wait on the process,
232
- // returning the wait status, while future calls error with "process
233
- // released" rather than "process done".
234
- p .state .CompareAndSwap (0 , uint32 (reason ))
235
- }
236
-
237
187
// ProcAttr holds the attributes that will be applied to a new process
238
188
// started by StartProcess.
239
189
type ProcAttr struct {
@@ -310,23 +260,54 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error)
310
260
// rendering it unusable in the future.
311
261
// Release only needs to be called if [Process.Wait] is not.
312
262
func (p * Process ) Release () error {
313
- // Note to future authors: the Release API is cursed.
314
- //
315
- // On Unix and Plan 9, Release sets p.Pid = -1. This is the only part of the
316
- // Process API that is not thread-safe, but it can't be changed now.
317
- //
318
- // On Windows, Release does _not_ modify p.Pid.
319
- //
320
- // On Windows, Wait calls Release after successfully waiting to
321
- // proactively clean up resources.
322
- //
323
- // On Unix and Plan 9, Wait also proactively cleans up resources, but
324
- // can not call Release, as Wait does not set p.Pid = -1.
325
- //
326
- // On Unix and Plan 9, calling Release a second time has no effect.
327
- //
328
- // On Windows, calling Release a second time returns EINVAL.
329
- return p .release ()
263
+ // Unfortunately, for historical reasons, on systems other
264
+ // than Windows, Release sets the Pid field to -1.
265
+ // This causes the race detector to report a problem
266
+ // on concurrent calls to Release, but we can't change it now.
267
+ if runtime .GOOS != "windows" {
268
+ p .Pid = - 1
269
+ }
270
+
271
+ oldStatus := p .doRelease (statusReleased )
272
+
273
+ // For backward compatibility, on Windows only,
274
+ // we return EINVAL on a second call to Release.
275
+ if runtime .GOOS == "windows" {
276
+ if oldStatus == statusReleased {
277
+ return syscall .EINVAL
278
+ }
279
+ }
280
+
281
+ return nil
282
+ }
283
+
284
+ // doRelease releases a [Process], setting the status to newStatus.
285
+ // If the previous status is not statusOK, this does nothing.
286
+ // It returns the previous status.
287
+ func (p * Process ) doRelease (newStatus processStatus ) processStatus {
288
+ for {
289
+ state := p .state .Load ()
290
+ oldStatus := processStatus (state )
291
+ if oldStatus != statusOK {
292
+ return oldStatus
293
+ }
294
+
295
+ if ! p .state .CompareAndSwap (state , uint32 (newStatus )) {
296
+ continue
297
+ }
298
+
299
+ // We have successfully released the Process.
300
+ // If it has a handle, release the reference we
301
+ // created in newHandleProcess.
302
+ if p .handle != nil {
303
+ // No need for more cleanup.
304
+ p .cleanup .Stop ()
305
+
306
+ p .handle .release ()
307
+ }
308
+
309
+ return statusOK
310
+ }
330
311
}
331
312
332
313
// Kill causes the [Process] to exit immediately. Kill does not wait until
0 commit comments