@@ -80,7 +80,42 @@ func wait(for pid: consuming pid_t) async throws -> ExitStatus {
80
80
}
81
81
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD)
82
82
/// 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
+ }
84
119
85
120
/// A condition variable used to suspend the waiter thread created by
86
121
/// `_createWaitThread()` when there are no child processes to await.
@@ -116,7 +151,7 @@ private let _createWaitThread: Void = {
116
151
var siginfo = siginfo_t ( )
117
152
if 0 == waitid ( P_ALL, 0 , & siginfo, WEXITED | WNOWAIT) {
118
153
if case let pid = siginfo. si_pid, pid != 0 {
119
- let continuation = _childProcessContinuations . withLock { childProcessContinuations in
154
+ let continuation = _withLockedChildProcessContinuations { childProcessContinuations, _ in
120
155
childProcessContinuations. removeValue ( forKey: pid)
121
156
}
122
157
@@ -137,7 +172,7 @@ private let _createWaitThread: Void = {
137
172
// newly-scheduled waiter process. (If this condition is spuriously
138
173
// woken, we'll just loop again, which is fine.) Note that we read errno
139
174
// outside the lock in case acquiring the lock perturbs it.
140
- _childProcessContinuations . withUnsafeUnderlyingLock { lock , childProcessContinuations in
175
+ _withLockedChildProcessContinuations { childProcessContinuations , lock in
141
176
if childProcessContinuations. isEmpty {
142
177
_ = pthread_cond_wait ( _waitThreadNoChildrenCondition, lock)
143
178
}
@@ -209,7 +244,7 @@ func wait(for pid: consuming pid_t) async throws -> ExitStatus {
209
244
_createWaitThread
210
245
211
246
return try await withCheckedThrowingContinuation { continuation in
212
- _childProcessContinuations . withLock { childProcessContinuations in
247
+ _withLockedChildProcessContinuations { childProcessContinuations, _ in
213
248
// We don't need to worry about a race condition here because waitid()
214
249
// does not clear the wait/zombie state of the child process. If it sees
215
250
// the child process has terminated and manages to acquire the lock before
0 commit comments