I’m calling a function in an external API with ccall. That C function makes an IO operation internally. If I call it with @async will the scheduler yield automatically, or do I have to hook this to libuv to make it async?
More generally, what makes an @async call yield to another task in parallel?
You have to hook it to libuv. The simplest way is probably to pass @cfunction(yield, Nothing, ()) as a callback function of type void (*yield)() to C, allowing you to call the Julia yield() function directly.
But then I don’t think you can do the IO while calling yield? I suppose all the IO has to go through the event loop in order to do them concurrently (non-blockingly).
IIUC, if you need to do IO without touching libuv’s IO interface, then you have to do it in a different thread. To ping Julia to come back to the C function in the main thread, you call uv_async_send(cond.handle) from the IO thread once the IO is finished, where this cond is created with Base.AsyncCondition. You can call Julia code wait(cond) in the thread that initialized Julia. During this wait(cond), Julia can run other tasks and block until uv_async_send. (Not sure if this is the only solution. It’s just what I know.)