@@ -90,16 +90,66 @@ type Process struct {
90
90
// Used only in modePID.
91
91
sigMu sync.RWMutex // avoid race between wait and signal
92
92
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.
98
110
//
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.
102
113
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
+ }
103
153
}
104
154
105
155
func newPIDProcess (pid int ) * Process {
@@ -112,10 +162,18 @@ func newPIDProcess(pid int) *Process {
112
162
}
113
163
114
164
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
+
115
173
p := & Process {
116
174
Pid : pid ,
117
175
mode : modeHandle ,
118
- handle : handle ,
176
+ handle : ph ,
119
177
}
120
178
p .state .Store (1 ) // 1 persistent reference
121
179
runtime .SetFinalizer (p , (* Process ).Release )
@@ -125,9 +183,7 @@ func newHandleProcess(pid int, handle uintptr) *Process {
125
183
func newDoneProcess (pid int ) * Process {
126
184
p := & Process {
127
185
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 ,
131
187
}
132
188
p .state .Store (uint64 (statusDone )) // No persistent reference, as there is no handle.
133
189
runtime .SetFinalizer (p , (* Process ).Release )
@@ -148,7 +204,11 @@ func (p *Process) handleTransientAcquire() (uintptr, processStatus) {
148
204
if ! p .state .CompareAndSwap (refs , new ) {
149
205
continue
150
206
}
151
- return p .handle , statusOK
207
+ h , ok := p .handle .acquire ()
208
+ if ! ok {
209
+ panic ("inconsistent reference counts" )
210
+ }
211
+ return h , statusOK
152
212
}
153
213
}
154
214
@@ -179,9 +239,7 @@ func (p *Process) handleTransientRelease() {
179
239
if ! p .state .CompareAndSwap (state , new ) {
180
240
continue
181
241
}
182
- if new &^processStatusMask == 0 {
183
- p .closeHandle ()
184
- }
242
+ p .handle .release ()
185
243
return
186
244
}
187
245
}
@@ -216,9 +274,7 @@ func (p *Process) handlePersistentRelease(reason processStatus) processStatus {
216
274
if ! p .state .CompareAndSwap (refs , new ) {
217
275
continue
218
276
}
219
- if new &^processStatusMask == 0 {
220
- p .closeHandle ()
221
- }
277
+ p .handle .release ()
222
278
return status
223
279
}
224
280
}
0 commit comments