I wrote a NIF which have function that can take a long time to run. To prevent the scheduler from being overloaded with long running functions (even if marked at dirty), I made my NIF threaded and it sends message back to erlang using enif_send.
I want to make those call synchronous from a “programmer point of view”. Similar to a Process.sleep() call.
I was wondering what was the best approach. To avoid messing with the caller message inbox and to have an isolated process to receive the message, I thought of using something like this:
task = Task.async(fn ->
call_nif(self())
receive do
<wait for nif response>
end
end)
Task.await(task)
My NIF is blocking and it is hard to make it non blocking, so I wrapped the entire function into a C thread, my NIF now spawn the thread and return immediately, then the thread sends the message. It seems to be a recommenced architecture in the documentation.
Threaded NIF - This is accomplished by dispatching the work to another thread managed by the NIF library, return from the NIF, and wait for the result. The thread can send the result back to the Erlang process using enif_send. Information about thread primitives is provided below.
Now my question is more about the user API which I want to make synchronous for API simplificy sake, so I thought of this task trick (which works fine). But I was wondering it was the right approach or if there was a more idiomatic way to do it.
In fact it is to make an async call synchronous in general, not really tied to a nif.
The calling process might have other messages in its inbox when I call this function, I didn’t want to mess with that. This nif does some GPU inference, it runs for about 5 seconds.
I know it could/should be an external port, but using nif I get all serialize/deserialize for free and it is very practical.