I have an
async function that I need to call inside a C callback, so I need to create a Task and wait for it to complete before I return from the callback. Currently I'm using a semaphore to block the thread, which seems a bit ugly, but the
Task API seems to deliberately avoid providing a way to block the current thread until a task completes. Is there a nicer way to do that?
For more context, the callback is from libgit2, asking for a user name and password, and I'm using the async version of
NSWindow.beginSheet() to prompt the user.
You are correct. This is not implemented because, e.g., there’s currently no way to guarantee priority donation to the task on Darwin.
If you confine your interaction with libgit2 to the main actor, you might be able to use continuations to resume execution of the callback after the sheet dismisses.
If you can create your own thread to run the blocking operation(s) on, that can be another approach. You generally don't want to block the threads that async tasks run on indefinitely, because there's a finite number of cooperative pool threads for tasks to use. For instance, you could have the thread do the blocking operation then resume the awaiting task's continuation. That scheme still has the problem Kyle alluded to that continuations can't propagate priority donation, but could be less bad than alternatives that involve blocking the main thread or cooperative pool.
Right. The most fundamental problem here is that libgit2 is going to block a thread waiting for this operation, and apparently it might do so from any call you make, so you need to make sure you're not tying things up in a way that could lead to deadlock. So calls into libgit2 should happen from a dedicated thread or
DispatchQueue. Once you're doing that, then sure, go ahead and use a semaphore.
Swift is never going to make this convenient, but if you're willing to accept the potential consequences, you can do it.