@@ -80,7 +80,42 @@ func wait(for pid: consuming pid_t) async throws -> ExitStatus {
8080}
8181#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD)
8282/// A mapping of awaited child PIDs to their corresponding Swift continuations.
83- private let _childProcessContinuations = LockedWith < pthread_mutex_t , [ pid_t : CheckedContinuation < ExitStatus , any Error > ] > ( )
83+ private nonisolated ( unsafe) let _childProcessContinuations = {
84+ let result = ManagedBuffer < [ pid_t : CheckedContinuation < ExitStatus , any Error > ] , pthread_mutex_t > . create (
85+ minimumCapacity: 1 ,
86+ makingHeaderWith: { _ in [ : ] }
87+ )
88+
89+ result. withUnsafeMutablePointers { sectionBounds, lock in
90+ _ = pthread_mutex_init ( lock, nil )
91+ }
92+
93+ return result
94+ } ( )
95+
96+ /// Access the value in `_childProcessContinuations` while guarded by its lock.
97+ ///
98+ /// - Parameters:
99+ /// - body: A closure to invoke while the lock is held.
100+ ///
101+ /// - Returns: Whatever is returned by `body`.
102+ ///
103+ /// - Throws: Whatever is thrown by `body`.
104+ private func _withLockedChildProcessContinuations< R> (
105+ _ body: (
106+ _ childProcessContinuations: inout [ pid_t : CheckedContinuation < ExitStatus , any Error > ] ,
107+ _ lock: UnsafeMutablePointer < pthread_mutex_t >
108+ ) throws -> R
109+ ) rethrows -> R {
110+ try _childProcessContinuations. withUnsafeMutablePointers { childProcessContinuations, lock in
111+ _ = pthread_mutex_lock ( lock)
112+ defer {
113+ _ = pthread_mutex_unlock ( lock)
114+ }
115+
116+ return try body ( & childProcessContinuations. pointee, lock)
117+ }
118+ }
84119
85120/// A condition variable used to suspend the waiter thread created by
86121/// `_createWaitThread()` when there are no child processes to await.
@@ -116,7 +151,7 @@ private let _createWaitThread: Void = {
116151 var siginfo = siginfo_t ( )
117152 if 0 == waitid ( P_ALL, 0 , & siginfo, WEXITED | WNOWAIT) {
118153 if case let pid = siginfo. si_pid, pid != 0 {
119- let continuation = _childProcessContinuations . withLock { childProcessContinuations in
154+ let continuation = _withLockedChildProcessContinuations { childProcessContinuations, _ in
120155 childProcessContinuations. removeValue ( forKey: pid)
121156 }
122157
@@ -137,7 +172,7 @@ private let _createWaitThread: Void = {
137172 // newly-scheduled waiter process. (If this condition is spuriously
138173 // woken, we'll just loop again, which is fine.) Note that we read errno
139174 // outside the lock in case acquiring the lock perturbs it.
140- _childProcessContinuations . withUnsafeUnderlyingLock { lock , childProcessContinuations in
175+ _withLockedChildProcessContinuations { childProcessContinuations , lock in
141176 if childProcessContinuations. isEmpty {
142177 _ = pthread_cond_wait ( _waitThreadNoChildrenCondition, lock)
143178 }
@@ -209,7 +244,7 @@ func wait(for pid: consuming pid_t) async throws -> ExitStatus {
209244 _createWaitThread
210245
211246 return try await withCheckedThrowingContinuation { continuation in
212- _childProcessContinuations . withLock { childProcessContinuations in
247+ _withLockedChildProcessContinuations { childProcessContinuations, _ in
213248 // We don't need to worry about a race condition here because waitid()
214249 // does not clear the wait/zombie state of the child process. If it sees
215250 // the child process has terminated and manages to acquire the lock before
0 commit comments