From 0fe8f3491bb54cfd9f7a5018cef0503260c97866 Mon Sep 17 00:00:00 2001 From: Ami Fischman Date: Fri, 19 May 2023 10:46:11 -0700 Subject: [PATCH] waitUntilExit: ignore RunLoop.run()'s return value (#4740) * waitUntilExit: ignore RunLoop.run()'s return value We've been seeing frequent (but not reproducible in isolation) failures in our builds where waitUntilExit returns but terminationStatus fails its precondition on hasFinished because isRunning is still true. This is a speculative fix, since I haven't been able to reproduce the failure in a self-contained test. This should be safe since Process.waitUntilExit isn't spec'd to relate to RunLoop. In particular there is no guarantee that "stopping" a RunLoop should cause waitUntilExit to return before the monitored process has exited. * Exclude libdispatch_init() on TARGET_OS_MAC because it duplicates the call from the static constructor https://github.com/apple/swift-corelibs-libdispatch/blob/7fb9d5ceea562d60fe34ec55b6b165ae5aca38eb/src/init.c#L56 --- CoreFoundation/Base.subproj/CFRuntime.c | 2 +- Sources/Foundation/Process.swift | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CoreFoundation/Base.subproj/CFRuntime.c b/CoreFoundation/Base.subproj/CFRuntime.c index 4aaa65ed3c..afdbf5d21e 100644 --- a/CoreFoundation/Base.subproj/CFRuntime.c +++ b/CoreFoundation/Base.subproj/CFRuntime.c @@ -1179,7 +1179,7 @@ void __CFInitialize(void) { if (!__CFInitialized && !__CFInitializing) { __CFInitializing = 1; -#if __HAS_DISPATCH__ +#if __HAS_DISPATCH__ && !TARGET_OS_MAC // libdispatch has to be initialized before CoreFoundation, so to avoid // issues with static initializer ordering, we are doing it explicitly. libdispatch_init(); diff --git a/Sources/Foundation/Process.swift b/Sources/Foundation/Process.swift index e064711296..542cc94cf1 100644 --- a/Sources/Foundation/Process.swift +++ b/Sources/Foundation/Process.swift @@ -1134,14 +1134,17 @@ open class Process: NSObject { open func waitUntilExit() { let runInterval = 0.05 let currentRunLoop = RunLoop.current - let checkRunLoop : () -> Bool = (currentRunLoop == self.runLoop) - ? { currentRunLoop.run(mode: .default, before: Date(timeIntervalSinceNow: runInterval)) } - : { currentRunLoop.run(until: Date(timeIntervalSinceNow: runInterval)); return true } - // update .runLoop to allow early wakeup. + let runRunLoop : () -> Void = (currentRunLoop == self.runLoop) + ? { currentRunLoop.run(mode: .default, before: Date(timeIntervalSinceNow: runInterval)) } + : { currentRunLoop.run(until: Date(timeIntervalSinceNow: runInterval)) } + // update .runLoop to allow early wakeup triggered by terminateRunLoop. self.runLoop = currentRunLoop - while self.isRunning && checkRunLoop() {} - + + while self.isRunning { + runRunLoop() + } + self.runLoop = nil self.runLoopSource = nil }