@@ -350,8 +350,8 @@ extension Configuration {
350350 )
351351
352352 internal func preSpawn< Result: ~ Copyable> (
353- _ work: ( PreSpawnArgs ) throws -> Result
354- ) throws -> Result {
353+ _ work: ( PreSpawnArgs ) async throws -> Result
354+ ) async throws -> Result {
355355 // Prepare environment
356356 let env = self . environment. createEnv ( )
357357 defer {
@@ -378,7 +378,7 @@ extension Configuration {
378378 if let groupsValue = self . platformOptions. supplementaryGroups {
379379 supplementaryGroups = groupsValue
380380 }
381- return try work (
381+ return try await work (
382382 (
383383 env: env,
384384 uidPtr: uidPtr,
@@ -415,11 +415,24 @@ internal typealias PlatformFileDescriptor = CInt
415415
416416#if !canImport(Darwin)
417417extension Configuration {
418+
419+ // @unchecked Sendable because we need to capture UnsafePointers
420+ // to send to another thread. While UnsafePointers are not
421+ // Sendable, we are not mutating them -- we only need these type
422+ // for C interface.
423+ internal struct SpawnContext : @unchecked Sendable {
424+ let argv : [ UnsafeMutablePointer < CChar > ? ]
425+ let env : [ UnsafeMutablePointer < CChar > ? ]
426+ let uidPtr : UnsafeMutablePointer < uid_t > ?
427+ let gidPtr : UnsafeMutablePointer < gid_t > ?
428+ let processGroupIDPtr : UnsafeMutablePointer < gid_t > ?
429+ }
430+
418431 internal func spawn(
419432 withInput inputPipe: consuming CreatedPipe ,
420433 outputPipe: consuming CreatedPipe ,
421434 errorPipe: consuming CreatedPipe
422- ) throws -> SpawnResult {
435+ ) async throws -> SpawnResult {
423436 // Ensure the waiter thread is running.
424437 #if os(Linux) || os(Android)
425438 _setupMonitorSignalHandler ( )
@@ -434,7 +447,7 @@ extension Configuration {
434447 var outputPipeBox : CreatedPipe ? = consume outputPipe
435448 var errorPipeBox : CreatedPipe ? = consume errorPipe
436449
437- return try self . preSpawn { args throws -> SpawnResult in
450+ return try await self . preSpawn { args throws -> SpawnResult in
438451 let ( env, uidPtr, gidPtr, supplementaryGroups) = args
439452
440453 var _inputPipe = inputPipeBox. take ( ) !
@@ -472,27 +485,34 @@ extension Configuration {
472485 ]
473486
474487 // Spawn
475- var pid : pid_t = 0
476- var processDescriptor : PlatformFileDescriptor = - 1
477- let spawnError : CInt = possibleExecutablePath. withCString { exePath in
478- return ( self . workingDirectory? . string) . withOptionalCString { workingDir in
479- return supplementaryGroups. withOptionalUnsafeBufferPointer { sgroups in
480- return fileDescriptors. withUnsafeBufferPointer { fds in
481- return _subprocess_fork_exec (
482- & pid,
483- & processDescriptor,
484- exePath,
485- workingDir,
486- fds. baseAddress!,
487- argv,
488- env,
489- uidPtr,
490- gidPtr,
491- processGroupIDPtr,
492- CInt ( supplementaryGroups? . count ?? 0 ) ,
493- sgroups? . baseAddress,
494- self . platformOptions. createSession ? 1 : 0
495- )
488+ let spawnContext = SpawnContext (
489+ argv: argv, env: env, uidPtr: uidPtr, gidPtr: gidPtr, processGroupIDPtr: processGroupIDPtr
490+ )
491+ let ( pid, processDescriptor, spawnError) = try await runOnBackgroundThread {
492+ return possibleExecutablePath. withCString { exePath in
493+ return ( self . workingDirectory? . string) . withOptionalCString { workingDir in
494+ return supplementaryGroups. withOptionalUnsafeBufferPointer { sgroups in
495+ return fileDescriptors. withUnsafeBufferPointer { fds in
496+ var pid : pid_t = 0
497+ var processDescriptor : PlatformFileDescriptor = - 1
498+
499+ let rc = _subprocess_fork_exec (
500+ & pid,
501+ & processDescriptor,
502+ exePath,
503+ workingDir,
504+ fds. baseAddress!,
505+ spawnContext. argv,
506+ spawnContext. env,
507+ spawnContext. uidPtr,
508+ spawnContext. gidPtr,
509+ spawnContext. processGroupIDPtr,
510+ CInt ( supplementaryGroups? . count ?? 0 ) ,
511+ sgroups? . baseAddress,
512+ self . platformOptions. createSession ? 1 : 0
513+ )
514+ return ( pid, processDescriptor, rc)
515+ }
496516 }
497517 }
498518 }
@@ -658,51 +678,6 @@ extension PlatformOptions: CustomStringConvertible, CustomDebugStringConvertible
658678 return self . description ( withIndent: 0 )
659679 }
660680}
661-
662- // Special keys used in Error's user dictionary
663- extension String {
664- static let debugDescriptionErrorKey = " DebugDescription "
665- }
666-
667- internal func pthread_create( _ body: @Sendable @escaping ( ) -> ( ) ) throws ( SubprocessError. UnderlyingError) -> pthread_t {
668- final class Context {
669- let body : @Sendable ( ) -> ( )
670- init ( body: @Sendable @escaping ( ) -> Void ) {
671- self . body = body
672- }
673- }
674- #if canImport(Glibc) || canImport(Musl)
675- func proc( _ context: UnsafeMutableRawPointer ? ) -> UnsafeMutableRawPointer ? {
676- ( Unmanaged < AnyObject > . fromOpaque ( context!) . takeRetainedValue ( ) as! Context ) . body ( )
677- return nil
678- }
679- #elseif canImport(Bionic)
680- func proc( _ context: UnsafeMutableRawPointer ) -> UnsafeMutableRawPointer {
681- ( Unmanaged < AnyObject > . fromOpaque ( context) . takeRetainedValue ( ) as! Context ) . body ( )
682- return context
683- }
684- #endif
685- #if (os(Linux) && canImport(Glibc)) || canImport(Bionic)
686- var thread = pthread_t ( )
687- #else
688- var thread : pthread_t ?
689- #endif
690- let rc = pthread_create (
691- & thread,
692- nil ,
693- proc,
694- Unmanaged . passRetained ( Context ( body: body) ) . toOpaque ( )
695- )
696- if rc != 0 {
697- throw SubprocessError . UnderlyingError ( rawValue: rc)
698- }
699- #if (os(Linux) && canImport(Glibc)) || canImport(Bionic)
700- return thread
701- #else
702- return thread!
703- #endif
704- }
705-
706681#endif // !canImport(Darwin)
707682
708683extension ProcessIdentifier {
0 commit comments