Make sure all relevant erl_nif functionality is represented in some way
If there is anything in particular you need, please comment below and we can work something out.
Primitives
Primitives are fully supported through Encoder.
- [x] enif_get_double()
- [x] enif_get_int()
- [x] enif_get_int64()
- [x] enif_get_long()
- [x] enif_get_uint()
- [x] enif_get_uint64()
- [x] enif_get_ulong()
- [x] enif_make_double()
- [x] enif_make_int64()
- [x] enif_make_long()
- [x] enif_make_int()
- [x] enif_make_uint()
- [x] enif_make_uint64()
- [x] enif_make_ulong()
Atoms
- [x] enif_get_atom()
- [ ] enif_get_atom_length() Will add if anyone has a legitimate use.
- [ ] enif_make_existing_atom() N/A
- [x] enif_make_existing_atom_len()
- [ ] enif_make_atom() N/A
- [x] enif_make_atom_len()
Lists
- [x] enif_make_reverse_list() NifTerm::list_reverse()
- [x] enif_get_list_cell() NifTerm::list_get_cell()
- [x] enif_get_list_length() NifTerm::list_length()
- [ ] enif_make_list()
- [x] enif_make_list_from_array()
- [x] enif_make_list_cell()
- [ ] enif_make_string_len()
Char lists
- [ ] enif_make_string()
- [ ] enif_get_string()
Maps
- [x] enif_get_map_size()
- [x] enif_get_map_value()
- [x] enif_make_map_put()
- [x] enif_make_map_remove()
- [x] enif_make_map_update()
- [x] enif_map_iterator_create()
- [x] enif_map_iterator_get_pair()
- [ ] enif_map_iterator_is_head()
- [ ] enif_map_iterator_is_tail()
- [x] enif_map_iterator_next()
- [ ] enif_map_iterator_prev()
- [x] enif_map_iterator_destroy()
- [x] enif_make_new_map()
Binaries
Supported through binary abstraction.
- [x] enif_alloc_binary()
- [x] enif_realloc_binary()
- [x] enif_inspect_binary()
- [x] enif_inspect_iolist_as_binary()
- [x] enif_make_new_binary()
- [x] enif_release_binary()
- [x] enif_make_binary()
- [x] enif_make_sub_binary()
Tuples
- [x] enif_get_tuple()
- [x] enif_make_tuple_from_array()
- [ ] enif_make_tuple() N/A
Resources
Exposed through resource abstraction.
- [x] enif_keep_resource()
- [x] enif_get_resource()
- [x] enif_sizeof_resource()
- [x] enif_release_resource()
- [x] enif_alloc_resource()
- [x] enif_make_resource()
- [x] enif_open_resource_type()
- [ ] enif_make_resource_binary()
Misc terms
- [ ] enif_make_pid()
- [ ] enif_make_ref()
- [ ] enif_get_local_pid()
- [ ] enif_get_local_port()
Term utils
- [x] enif_make_copy()
- [x] enif_term_to_binary()
- [x] enif_binary_to_term()
- [x] enif_compare()
- [x] enif_is_identical()
- [x] enif_snprintf()
- [ ] enif_port_command()
- [ ] enif_is_port_alive()
- [ ] enif_is_process_alive()
Type tests
Exposed as methods on NifTerm.
- [x] enif_is_atom()
- [x] enif_is_binary()
- [x] enif_is_list()
- [x] enif_is_pid()
- [x] enif_is_port()
- [x] enif_is_ref()
- [x] enif_is_tuple()
- [x] enif_is_empty_list()
- [x] enif_is_exception()
- [x] enif_is_fun()
- [x] enif_is_map()
- [x] enif_is_number()
Scheduling
- [x] enif_consume_timeslice()
- [ ] enif_schedule_nif() See this
Exceptions
- [ ] enif_has_pending_exception() Useless for us since we don't expose raise_exception to the user.
Supported through rustler::Error. It is undesirable to expose these directly:
- [x] enif_make_badarg()
- [x] enif_raise_exception()
Env
- [x] enif_free_env()
- [x] enif_clear_env()
- [x] enif_alloc_env()
- [x] enif_self()
- [x] enif_send() See this
Time
- [ ] enif_cpu_time()
- [ ] enif_now_time()
- [ ] enif_monotonic_time()
- [ ] enif_time_offset()
- [ ] enif_convert_time_unit()
Misc
- [ ] enif_getenv()
- [ ] enif_alloc() N/A
- [ ] enif_free() N/A
- [ ] enif_priv_data() No plans to support.
- [ ] enif_system_info()
- [ ] enif_make_unique_integer()
- [ ] enif_is_current_process_alive()
Threading
Rust has its own threading APIs. There are no plans to support this.
- [ ] enif_rwlock_tryrlock()
- [ ] enif_rwlock_tryrwlock()
- [ ] enif_thread_create()
- [ ] enif_thread_join()
- [ ] enif_tsd_key_create()
- [ ] enif_mutex_trylock()
- [ ] enif_thread_type()
- [ ] enif_equal_tids()
- [ ] enif_cond_broadcast()
- [ ] enif_cond_destroy()
- [ ] enif_cond_signal()
- [ ] enif_cond_wait()
- [ ] enif_mutex_destroy()
- [ ] enif_mutex_lock()
- [ ] enif_mutex_unlock()
- [ ] enif_rwlock_destroy()
- [ ] enif_rwlock_rlock()
- [ ] enif_rwlock_runlock()
- [ ] enif_rwlock_rwlock()
- [ ] enif_rwlock_rwunlock()
- [ ] enif_thread_exit()
- [ ] enif_thread_opts_destroy()
- [ ] enif_tsd_key_destroy()
- [ ] enif_tsd_set()
- [ ] enif_tsd_get()
- [ ] enif_cond_create()
- [ ] enif_mutex_create()
- [ ] enif_rwlock_create()
- [ ] enif_thread_opts_create()
- [ ] enif_thread_self()
Problems
- enif_send(): Documentation requires calling env to be passed when called within a NIF call. There is no way to enforce this at compile-time.
- enif_schedule_nif(): Requires return value to be returned from the NIF call. There is no straightforward way to enforce this at compile-time.
Probably going to put these features under a cargo feature since they require additional caution from the user.
We might want to open an issue on OTP about these problems?
For enif_send(), we can just always require an env. I'm not sure what kind of situation would lead a user to want to send a message without having an env handy.
@jorendorff The erl_nif docs say: "env: The environment of the calling process. Must be NULL only if calling from a created thread"
This means that by not supplying an env when doing send from within a NIF call, we are technically breaking an API invariant.
@hansihe We always supply an env, so I think we're OK.
@jorendorff Just checked the current implementation. It is wrong.
The docs say the first env should be the env of the calling process if called from a nif, null if and only if from a calling thread. The second env should be the env the message term belongs to.
The message term env is never supposed to be passed as the first argument.
You're right. We could fix it using thread-local storage; I don't see any other way.
I checked the source a little while ago. They don't use the first env for anything other than to improve tracing functionality.
So, passing null always is fine with the current BEAM implementation. The question is if we want to take that risk, it might change in the future.
It seems best to code to the documented API. I'll patch it.
This API list hasn't been updated for quite a while and is missing all NIF API functions for OTP>=20.
I wrote a small helper to find all documented NIF functions. The escript allows filtering by version prefix.