I've written a global function that allows me to synchronously wait for the completion of an async task (for experimentation purposes only!) with the help of a DispatchSemaphore
.
///
/// synchronously waits for the completion of an asynchronous closure
/// - parameter handler: the task to run, asynchronously
/// - note: don't use this in production code, it violates
/// the Swift runtime contract of "forward" progress, it's never
/// safe to block a thread and wait for another thread to unblock it
/// (on a single core system, this may hang forever)
///
func sync(handler: @escaping () async -> Void) {
let sema = DispatchSemaphore(value: 0)
// default `Task.Priority` to `asyncDetached` is `nil`
Task.detached {
await handler()
sema.signal()
}
// blocks the current thread, waiting for the async Task to finish
sema.wait()
}
I noticed that if I just use an Task
block to launch the asynchronous tasks a deadlock arises, presumably because sema.wait()
is blocking the current thread, preventing the Task
block from ever running (as normal Task
blocks seem to inherit the current thread), so it will just wait forever.
Using Task.detached
does not seem to block the thread, making the above code work.
This appears to be related to the Task.Priority?
to which the detached call is assigned.
The default parameter value is to which is nil
when calling Task.detached
.
This can be customized to a different priority level, indicating the dispatch QoS.
My questions therefore is: what thread does Task.Priority? = nil
relate to? It does not appear to ever be the same thread as where it was launched from. Does Task.Priority? = nil
indicate to the Swift runtime to always run on a different thread?
EDIT
Follow on question - how do Task
and Task.detached
differ in regard to threading?