@@ -157,17 +157,25 @@ extension Configuration {
157157 @available ( SubprocessSpan, * )
158158 #endif
159159 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 {
164164 // Instead of checking if every possible executable path
165165 // is valid, spawn each directly and catch ENOENT
166166 let possiblePaths = self . executable. possibleExecutablePaths (
167167 withPathValue: self . environment. pathValue ( )
168168 )
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
170174 let ( env, uidPtr, gidPtr, supplementaryGroups) = args
175+ let _inputPipe = inputPipeBox!. take ( ) !
176+ let _outputPipe = outputPipeBox!. take ( ) !
177+ let _errorPipe = errorPipeBox!. take ( ) !
178+
171179 for possibleExecutablePath in possiblePaths {
172180 var pid : pid_t = 0
173181
@@ -187,67 +195,68 @@ extension Configuration {
187195 defer {
188196 posix_spawn_file_actions_destroy ( & fileActions)
189197 }
198+
190199 // Input
191200 var result : Int32 = - 1
192- if let inputRead = inputPipe . readFileDescriptor {
201+ if let inputRead = _inputPipe . readFileDescriptor {
193202 result = posix_spawn_file_actions_adddup2 ( & fileActions, inputRead. platformDescriptor, 0 )
194203 guard result == 0 else {
195- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
204+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
196205 throw SubprocessError (
197206 code: . init( . spawnFailed) ,
198207 underlyingError: . init( rawValue: result)
199208 )
200209 }
201210 }
202- if let inputWrite = inputPipe . writeFileDescriptor {
211+ if let inputWrite = _inputPipe . writeFileDescriptor {
203212 // Close parent side
204213 result = posix_spawn_file_actions_addclose ( & fileActions, inputWrite. platformDescriptor)
205214 guard result == 0 else {
206- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
215+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
207216 throw SubprocessError (
208217 code: . init( . spawnFailed) ,
209218 underlyingError: . init( rawValue: result)
210219 )
211220 }
212221 }
213222 // Output
214- if let outputWrite = outputPipe . writeFileDescriptor {
223+ if let outputWrite = _outputPipe . writeFileDescriptor {
215224 result = posix_spawn_file_actions_adddup2 ( & fileActions, outputWrite. platformDescriptor, 1 )
216225 guard result == 0 else {
217- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
226+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
218227 throw SubprocessError (
219228 code: . init( . spawnFailed) ,
220229 underlyingError: . init( rawValue: result)
221230 )
222231 }
223232 }
224- if let outputRead = outputPipe . readFileDescriptor {
233+ if let outputRead = _outputPipe . readFileDescriptor {
225234 // Close parent side
226235 result = posix_spawn_file_actions_addclose ( & fileActions, outputRead. platformDescriptor)
227236 guard result == 0 else {
228- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
237+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
229238 throw SubprocessError (
230239 code: . init( . spawnFailed) ,
231240 underlyingError: . init( rawValue: result)
232241 )
233242 }
234243 }
235244 // Error
236- if let errorWrite = errorPipe . writeFileDescriptor {
245+ if let errorWrite = _errorPipe . writeFileDescriptor {
237246 result = posix_spawn_file_actions_adddup2 ( & fileActions, errorWrite. platformDescriptor, 2 )
238247 guard result == 0 else {
239- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
248+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
240249 throw SubprocessError (
241250 code: . init( . spawnFailed) ,
242251 underlyingError: . init( rawValue: result)
243252 )
244253 }
245254 }
246- if let errorRead = errorPipe . readFileDescriptor {
255+ if let errorRead = _errorPipe . readFileDescriptor {
247256 // Close parent side
248257 result = posix_spawn_file_actions_addclose ( & fileActions, errorRead. platformDescriptor)
249258 guard result == 0 else {
250- try self . cleanupPreSpawn ( input: inputPipe , output: outputPipe , error: errorPipe )
259+ try self . cleanupPreSpawn ( input: _inputPipe , output: _outputPipe , error: _errorPipe )
251260 throw SubprocessError (
252261 code: . init( . spawnFailed) ,
253262 underlyingError: . init( rawValue: result)
@@ -290,7 +299,7 @@ extension Configuration {
290299
291300 // Error handling
292301 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 )
294303 if spawnAttributeError != 0 {
295304 throw SubprocessError (
296305 code: . init( . spawnFailed) ,
@@ -336,34 +345,36 @@ extension Configuration {
336345 }
337346 // Throw all other errors
338347 try self . cleanupPreSpawn (
339- input: inputPipe ,
340- output: outputPipe ,
341- error: errorPipe
348+ input: _inputPipe ,
349+ output: _outputPipe ,
350+ error: _errorPipe
342351 )
343352 throw SubprocessError (
344353 code: . init( . spawnFailed) ,
345354 underlyingError: . init( rawValue: spawnError)
346355 )
347356 }
348357
349- func captureError( _ work: ( ) throws -> Void ) -> ( any Swift . Error ) ? {
350- do {
351- try work ( )
352- return nil
353- } catch {
354- return error
355- }
356- }
357358 // 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
360364 }
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
363370 }
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
366376 }
377+
367378 if let inputCloseError = inputCloseError {
368379 throw inputCloseError
369380 }
@@ -374,17 +385,23 @@ extension Configuration {
374385 throw errorCloseError
375386 }
376387
377- return Execution (
388+ let execution = Execution (
378389 processIdentifier: . init( value: pid)
379390 )
391+ return SpawnResult (
392+ execution: execution,
393+ inputPipe: _inputPipe,
394+ outputPipe: _outputPipe,
395+ errorPipe: _errorPipe
396+ )
380397 }
381398
382399 // If we reach this point, it means either the executable path
383400 // or working directory is not valid. Since posix_spawn does not
384401 // provide which one is not valid, here we make a best effort guess
385402 // by checking whether the working directory is valid. This technically
386403 // 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 )
388405 let workingDirectory = self . workingDirectory. string
389406 guard Configuration . pathAccessible ( workingDirectory, mode: F_OK) else {
390407 throw SubprocessError (
0 commit comments