@@ -157,17 +157,25 @@ extension Configuration {
157
157
@available ( SubprocessSpan, * )
158
158
#endif
159
159
internal func spawn(
160
- withInput inputPipe: CreatedPipe ,
161
- outputPipe: CreatedPipe ,
162
- errorPipe: CreatedPipe
163
- ) throws -> Execution {
160
+ withInput inputPipe: consuming CreatedPipe ,
161
+ outputPipe: consuming CreatedPipe ,
162
+ errorPipe: consuming CreatedPipe
163
+ ) throws -> SpawnResult {
164
164
// Instead of checking if every possible executable path
165
165
// is valid, spawn each directly and catch ENOENT
166
166
let possiblePaths = self . executable. possibleExecutablePaths (
167
167
withPathValue: self . environment. pathValue ( )
168
168
)
169
- return try self . preSpawn { args throws -> Execution in
169
+ var inputPipeBox : CreatedPipe ? ? = consume inputPipe
170
+ var outputPipeBox : CreatedPipe ? ? = consume outputPipe
171
+ var errorPipeBox : CreatedPipe ? ? = consume errorPipe
172
+
173
+ return try self . preSpawn { args throws -> SpawnResult in
170
174
let ( env, uidPtr, gidPtr, supplementaryGroups) = args
175
+ let _inputPipe = inputPipeBox!. take ( ) !
176
+ let _outputPipe = outputPipeBox!. take ( ) !
177
+ let _errorPipe = errorPipeBox!. take ( ) !
178
+
171
179
for possibleExecutablePath in possiblePaths {
172
180
var pid : pid_t = 0
173
181
@@ -187,67 +195,68 @@ extension Configuration {
187
195
defer {
188
196
posix_spawn_file_actions_destroy ( & fileActions)
189
197
}
198
+
190
199
// Input
191
200
var result : Int32 = - 1
192
- if let inputRead = inputPipe . readFileDescriptor {
201
+ if let inputRead = _inputPipe . readFileDescriptor {
193
202
result = posix_spawn_file_actions_adddup2 ( & fileActions, inputRead. platformDescriptor, 0 )
194
203
guard result == 0 else {
195
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
204
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
196
205
throw SubprocessError (
197
206
code: . init( . spawnFailed) ,
198
207
underlyingError: . init( rawValue: result)
199
208
)
200
209
}
201
210
}
202
- if let inputWrite = inputPipe . writeFileDescriptor {
211
+ if let inputWrite = _inputPipe . writeFileDescriptor {
203
212
// Close parent side
204
213
result = posix_spawn_file_actions_addclose ( & fileActions, inputWrite. platformDescriptor)
205
214
guard result == 0 else {
206
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
215
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
207
216
throw SubprocessError (
208
217
code: . init( . spawnFailed) ,
209
218
underlyingError: . init( rawValue: result)
210
219
)
211
220
}
212
221
}
213
222
// Output
214
- if let outputWrite = outputPipe . writeFileDescriptor {
223
+ if let outputWrite = _outputPipe . writeFileDescriptor {
215
224
result = posix_spawn_file_actions_adddup2 ( & fileActions, outputWrite. platformDescriptor, 1 )
216
225
guard result == 0 else {
217
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
226
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
218
227
throw SubprocessError (
219
228
code: . init( . spawnFailed) ,
220
229
underlyingError: . init( rawValue: result)
221
230
)
222
231
}
223
232
}
224
- if let outputRead = outputPipe . readFileDescriptor {
233
+ if let outputRead = _outputPipe . readFileDescriptor {
225
234
// Close parent side
226
235
result = posix_spawn_file_actions_addclose ( & fileActions, outputRead. platformDescriptor)
227
236
guard result == 0 else {
228
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
237
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
229
238
throw SubprocessError (
230
239
code: . init( . spawnFailed) ,
231
240
underlyingError: . init( rawValue: result)
232
241
)
233
242
}
234
243
}
235
244
// Error
236
- if let errorWrite = errorPipe . writeFileDescriptor {
245
+ if let errorWrite = _errorPipe . writeFileDescriptor {
237
246
result = posix_spawn_file_actions_adddup2 ( & fileActions, errorWrite. platformDescriptor, 2 )
238
247
guard result == 0 else {
239
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
248
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
240
249
throw SubprocessError (
241
250
code: . init( . spawnFailed) ,
242
251
underlyingError: . init( rawValue: result)
243
252
)
244
253
}
245
254
}
246
- if let errorRead = errorPipe . readFileDescriptor {
255
+ if let errorRead = _errorPipe . readFileDescriptor {
247
256
// Close parent side
248
257
result = posix_spawn_file_actions_addclose ( & fileActions, errorRead. platformDescriptor)
249
258
guard result == 0 else {
250
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
259
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
251
260
throw SubprocessError (
252
261
code: . init( . spawnFailed) ,
253
262
underlyingError: . init( rawValue: result)
@@ -290,7 +299,7 @@ extension Configuration {
290
299
291
300
// Error handling
292
301
if chdirError != 0 || spawnAttributeError != 0 {
293
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
302
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
294
303
if spawnAttributeError != 0 {
295
304
throw SubprocessError (
296
305
code: . init( . spawnFailed) ,
@@ -336,34 +345,36 @@ extension Configuration {
336
345
}
337
346
// Throw all other errors
338
347
try self . cleanupPreSpawn (
339
- input: inputPipe ,
340
- output: outputPipe ,
341
- error: errorPipe
348
+ input: _inputPipe ,
349
+ output: _outputPipe ,
350
+ error: _errorPipe
342
351
)
343
352
throw SubprocessError (
344
353
code: . init( . spawnFailed) ,
345
354
underlyingError: . init( rawValue: spawnError)
346
355
)
347
356
}
348
357
349
- func captureError( _ work: ( ) throws -> Void ) -> ( any Swift . Error ) ? {
350
- do {
351
- try work ( )
352
- return nil
353
- } catch {
354
- return error
355
- }
356
- }
357
358
// After spawn finishes, close all child side fds
358
- let inputCloseError = captureError {
359
- try inputPipe. readFileDescriptor? . safelyClose ( )
359
+ var inputCloseError : ( any Swift . Error ) ? = nil
360
+ do {
361
+ try _inputPipe. readFileDescriptor? . safelyClose ( )
362
+ } catch {
363
+ inputCloseError = error
360
364
}
361
- let outputCloseError = captureError {
362
- try outputPipe. writeFileDescriptor? . safelyClose ( )
365
+ var outputCloseError : ( any Swift . Error ) ? = nil
366
+ do {
367
+ try _outputPipe. writeFileDescriptor? . safelyClose ( )
368
+ } catch {
369
+ outputCloseError = error
363
370
}
364
- let errorCloseError = captureError {
365
- try errorPipe. writeFileDescriptor? . safelyClose ( )
371
+ var errorCloseError : ( any Swift . Error ) ? = nil
372
+ do {
373
+ try _errorPipe. writeFileDescriptor? . safelyClose ( )
374
+ } catch {
375
+ errorCloseError = error
366
376
}
377
+
367
378
if let inputCloseError = inputCloseError {
368
379
throw inputCloseError
369
380
}
@@ -374,17 +385,23 @@ extension Configuration {
374
385
throw errorCloseError
375
386
}
376
387
377
- return Execution (
388
+ let execution = Execution (
378
389
processIdentifier: . init( value: pid)
379
390
)
391
+ return SpawnResult (
392
+ execution: execution,
393
+ inputPipe: _inputPipe,
394
+ outputPipe: _outputPipe,
395
+ errorPipe: _errorPipe
396
+ )
380
397
}
381
398
382
399
// If we reach this point, it means either the executable path
383
400
// or working directory is not valid. Since posix_spawn does not
384
401
// provide which one is not valid, here we make a best effort guess
385
402
// by checking whether the working directory is valid. This technically
386
403
// still causes TOUTOC issue, but it's the best we can do for error recovery.
387
- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
404
+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
388
405
let workingDirectory = self . workingDirectory. string
389
406
guard Configuration . pathAccessible ( workingDirectory, mode: F_OK) else {
390
407
throw SubprocessError (
0 commit comments