Commit b66032f
committed
Fix: Replace Process.waitUntilExit() with DispatchGroup to prevent hangs
The waitUntilExit() call was causing hangs when invoked from Swift concurrency's
cooperative thread pool. This is because waitUntilExit() runs the current thread's
runloop in NSDefaultRunLoopMode while waiting, which can cause unexpected behavior
when called from arbitrary threads or thread pool contexts.
The issue occurs because:
1. waitUntilExit() runs the runloop in default mode, allowing timers and callbacks
to fire unexpectedly (see Mike Ash's article on dangerous Cocoa calls)
2. Swift concurrency's cooperative thread pool doesn't guarantee which thread
executes a given task, making runloop-dependent APIs unreliable
3. The runloop may not be properly configured on thread pool threads
The fix uses a DispatchGroup that is entered before starting the process and
left in the process's terminationHandler. The close() method then calls wait()
on the group, which is a simple blocking call without runloop involvement.
This approach is safe because:
- It doesn't depend on runloop infrastructure
- It works correctly regardless of which thread it's called from
- It provides clean synchronization with process termination
References:
- https://stackoverflow.com/questions/34996937/how-to-safely-use-nstask-waituntilexit-off-the-main-thread
- https://mikeash.com/pyblog/friday-qa-2009-11-13-dangerous-cocoa-calls.html1 parent 41a3cc0 commit b66032f
1 file changed
+7
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
130 | 132 | | |
131 | 133 | | |
132 | 134 | | |
| |||
153 | 155 | | |
154 | 156 | | |
155 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
156 | 162 | | |
157 | 163 | | |
158 | 164 | | |
| |||
186 | 192 | | |
187 | 193 | | |
188 | 194 | | |
189 | | - | |
| 195 | + | |
190 | 196 | | |
191 | 197 | | |
192 | 198 | | |
| |||
0 commit comments