RustlerElixirFun
With Rustler, it became a lot easier to create well-behaving Natively-Implemented-Functions (NIFs).
However, one question which arises from time to time (here, here, here, here, …), is how to call back into Elixir code from within native code: If you pass an anonymous function to a NIF, then how can we use this function inside the NIF?
The short answer: This is not supported.
The longer answer: … But if you get ready to jump through some hoops, you can still make it work!
The gist of it: (Thanks to Erlang Forums user @robashton and GitHub user @tessi’s explanations for inspiration!)
- In native code, create a manual ‘future’ (a mutex wrapping a potentially-empty value, and a condition variable).
- Wrap this ‘future’ into a reference which can be passed back to Elixir.
- Send
{fun, params, future_ref}
to a particular process (or pool of processes) which you’ve been running. - Block the native code until the future is filled. (Or a timeout is triggered)
- In the elixir GenServer, run the function, (handle errors) and once the result is obtained, call a second NIF with the result and the future_ref.
- This NIF will ‘fill’ the future with the passed result and immediately return.
- Now the original native code will continue.
Or, in a diagram:
Until now, there were a couple of projects which did this manually, but it seemed to make sense to start working on a library to abstract this pattern once and for all.
This way, we can make sure it works well (no edge cases) and as efficient as it might be, with both a single GenServer you might run, as well as a full-fledged pool.
The project can be found at https://github.com/Qqwy/elixir-rustler_elixir_fun.
Work on RustlerElixirFun is still ongoing, but feedback would already be much appreciated.