Talks
Elixir and the Outside World
- Ports
- erl_interface
- C Nodes
- Port Drivers
- Native Implemented Functions (NIFs)
NIFs
- Called just like any other Erlang/Elixir code
- NIFs live in a dynamically loaded library (.so or .dll)
- NIFs replace Erlang/Elixir functions of the same name/arity at module load time
Functionality
- Read and write Erlang terms
- Binaries
- Resource objects
- Threads and concurrency
Example
defmodule Json do
@on_load :init
def init do
dylib = :code.priv_dir(:fast_json) ++ '/libfast_json'
:erlang.load_nif(dylib, 0)
end
def encode(_data), do: :erlang.nif_error(:nif_not_loaded)
def decode(_data), do: :erlang.nif_error(:nif_not_loaded)
end
Resource Objects
- Resource objects are a safe way to return pointers to native data structures from a NIF
- It can be stored and passed between processes on the same node
- the only real end usage is to pass it back as an argument to a NIF
- A resource object is not deallocated until the last handle term is garbage collected by the VM
Rescheduling
- The Erlang NIF API provides a way for a NIF to reschedule itself directly with
enif_schedule_nif
- When combined with
rustler::schedule::consume_timeslice
it allows all the chunking to be done directly in the NIF (No supported yet)
Threaded NIFs
- Spawn a separate OS thread to do the work
- Send the result as a message to the calling process
Dirty NIFs
- Allows you to call a NIF without worrying about blocking a normal scheduler
- By default you will have the same number of dirty schedulers as normal schedulers
Elixir with Rust #rust rustler
#[rustler::nif(schedule = "DirtyCpu" | "DirtyIo")]
pub fn dirty_cpu() -> Atom {
let duration = Duration::from_millis(100);
std::thread::sleep(duration);
atoms::ok()
}
serde_rustler JSON Example
use serde_bytes::Bytes;
use serde_rustler::{from_term, to_term, Deserializer, Serializer};
use serde_transcode::transcode;
/// Deserializes a JSON string into an Elixir term.
pub fn decode<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let json_bytes: &[u8] = from_term(args[0])?;
let mut de = serde_json::Deserializer::from_slice(json_bytes);
let ser = Serializer::from(env);
transcode(&mut de, ser).map_err(|err| err.into())
}
/// Serializes an Elixir term into a compact JSON string.
pub fn encode_compact<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let de = Deserializer::from(args[0]);
let mut ser_vec = Vec::new();
let mut ser = serde_json::Serializer::new(&mut ser_vec);
transcode(de, &mut ser).or(Err(NifError::RaiseAtom("transcode error")))?;
to_term(env, Bytes::new(&ser_vec)).map_err(|err| err.into())
}