Right way to Asynchronously wait for a Process to terminate

Sorry for asking this many questions here, I am still learning...

So I am trying to solve the following: I want to run a process and an async function in "parallel" and return if either some event happens (I am tailing a log file to check whether the start of the process was successful) or the process terminated. Basically I'd like to do something like this:

let process = Process() // this will be needed later
process.executableURL = URL(string: "myexec")
process.arguments = [] // args
let success = await withTaskGroup(of: Bool.self) { group in
    group.addTask {
        // using process here is probably unsafe
        async let done: Void = withCheckedContinuation { c in
            process.terminationHandler = { _ in
                c.resume()
            }
        }
        do {
            try process.run()
        } catch {
            // not sure whether this is correct -- I might need to wrap this in a task and cancel instead?
            await done
            return false
        }
        await done // might never return
        return false
    }
    group.addTask {
        await self.asyncFuncReturningBool()
    }
    
    let res = await group.next()
    group.cancelAll() // though I want to keep the process running if it didn't terminate
    if !res! {
        // should be ok, even if the process is already terminated
        process.terminate()
    }
    return res
}

The above does compile with warnings (Process is not Sendable).

I think I understand why the above is problematic. But what would be the most swift-like way of doing the above?

I'm not sure why anyone didn't respond to you here, but for the next person who does this, I found that simply adding an extension to Process is the simplest:

import Foundation

extension Process {
    func waitUntilExitAsync() async {
        await withCheckedContinuation { c in
            self.terminationHandler = { _ in
                c.resume()
            }
        }
    }
}

So you build your Process somewhere and add the executableURL, arguments, and whatever else, run it, and then wait for exit using the new async method:

let process = Process()
process.executableURL = URL(string: "myexec")
process.arguments = [] // args
process.run()
await process.waitUntilExitAsync()

Hope this helps someone else! It works nicely with the new swift-foundation in 6.0 as well!

2 Likes