55#include < cstdint>
66#include < cstdlib>
77#include < cstring>
8+ #include < functional>
89#include < map>
910#include < memory>
1011#include < optional>
@@ -47,7 +48,10 @@ template <typename T, typename SFINAE = void> struct Encoder;
4748
4849namespace __private__ {
4950std::vector<ErlNifFunc> &get_erl_nif_funcs ();
50- int load (ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info);
51+ void init_atoms (ErlNifEnv *env);
52+ bool init_resources (ErlNifEnv *env);
53+ int load (ErlNifEnv *, void **, ERL_NIF_TERM) noexcept ;
54+ void unload (ErlNifEnv *, void *) noexcept ;
5155} // namespace __private__
5256
5357// Definitions
@@ -97,8 +101,7 @@ class Atom {
97101 friend struct Decoder <Atom>;
98102 friend struct ::std::hash<Atom>;
99103
100- friend int __private__::load (ErlNifEnv *env, void **priv_data,
101- ERL_NIF_TERM load_info);
104+ friend void __private__::init_atoms (ErlNifEnv *env);
102105
103106 // We accumulate all globally defined atom objects and create the
104107 // terms upfront as part of init (called from the NIF load callback).
@@ -1187,6 +1190,12 @@ template <typename T> void raise(ErlNifEnv *env, const T &value) {
11871190// Mechanism for accumulating information via static object definitions.
11881191class Registration {
11891192public:
1193+ // A function compatible with the load callback of Erlang's NIFs.
1194+ using LoadCallback = std::function<void (ErlNifEnv *, void **, fine::Term)>;
1195+
1196+ // A function compatible with the unload callback of Erlang's NIFs.
1197+ using UnloadCallback = std::function<void (ErlNifEnv *, void *)>;
1198+
11901199 template <typename T>
11911200 static Registration register_resource (const char *name) {
11921201 Registration::resources.push_back ({&fine::ResourcePtr<T>::resource_type,
@@ -1200,6 +1209,26 @@ class Registration {
12001209 return {};
12011210 }
12021211
1212+ // Registers a load callback.
1213+ static Registration register_load (LoadCallback callback) {
1214+ if (erl_nif_load_callback) {
1215+ throw std::logic_error (" load callback already registered" );
1216+ }
1217+
1218+ Registration::erl_nif_load_callback = callback;
1219+ return {};
1220+ }
1221+
1222+ // Registers an unload callback.
1223+ static Registration register_unload (UnloadCallback callback) {
1224+ if (erl_nif_unload_callback) {
1225+ throw std::logic_error (" unload callback already registered" );
1226+ }
1227+
1228+ Registration::erl_nif_unload_callback = callback;
1229+ return {};
1230+ }
1231+
12031232private:
12041233 static bool init_resources (ErlNifEnv *env) {
12051234 for (const auto &[resource_type_ptr, name, dtor] :
@@ -1220,15 +1249,18 @@ class Registration {
12201249 }
12211250
12221251 friend std::vector<ErlNifFunc> &__private__::get_erl_nif_funcs ();
1252+ friend int __private__::load (ErlNifEnv *, void **, ERL_NIF_TERM) noexcept ;
1253+ friend void __private__::unload (ErlNifEnv *, void *) noexcept ;
12231254
1224- friend int __private__::load (ErlNifEnv *env, void **priv_data,
1225- ERL_NIF_TERM load_info);
1255+ friend bool __private__::init_resources (ErlNifEnv *env);
12261256
12271257 inline static std::vector<std::tuple<ErlNifResourceType **, const char *,
12281258 void (*)(ErlNifEnv *, void *)>>
12291259 resources = {};
12301260
12311261 inline static std::vector<ErlNifFunc> erl_nif_funcs = {};
1262+ inline static LoadCallback erl_nif_load_callback = {};
1263+ inline static UnloadCallback erl_nif_unload_callback = {};
12321264};
12331265
12341266// NIF definitions
@@ -1295,23 +1327,49 @@ constexpr unsigned int nif_arity(Ret (*)(Args...)) {
12951327}
12961328
12971329namespace __private__ {
1330+ void init_atoms (ErlNifEnv *env) { fine::Atom::init_atoms (env); }
1331+
1332+ bool init_resources (ErlNifEnv *env) {
1333+ return fine::Registration::init_resources (env);
1334+ }
1335+
12981336inline std::vector<ErlNifFunc> &get_erl_nif_funcs () {
12991337 return Registration::erl_nif_funcs;
13001338}
13011339
1302- inline int load (ErlNifEnv *env, void **, ERL_NIF_TERM) {
1303- Atom::init_atoms (env);
1340+ inline int load (ErlNifEnv *caller_env, void **priv_data,
1341+ ERL_NIF_TERM load_info) noexcept {
1342+ init_atoms (caller_env);
13041343
1305- if (!Registration::init_resources (env)) {
1344+ if (!init_resources (caller_env)) {
1345+ return -1 ;
1346+ }
1347+
1348+ try {
1349+ if (fine::Registration::erl_nif_load_callback) {
1350+ std::invoke (fine::Registration::erl_nif_load_callback, caller_env,
1351+ priv_data, load_info);
1352+ }
1353+ } catch (const std::exception &e) {
1354+ enif_fprintf (stderr, " unhandled exception: %s\n " , e.what ());
1355+ return -1 ;
1356+ } catch (...) {
1357+ enif_fprintf (stderr, " unhandled throw\n " );
13061358 return -1 ;
13071359 }
13081360
13091361 return 0 ;
13101362}
1363+
1364+ inline void unload (ErlNifEnv *caller_env, void *priv_data) noexcept {
1365+ if (fine::Registration::erl_nif_unload_callback) {
1366+ std::invoke (fine::Registration::erl_nif_unload_callback, caller_env,
1367+ priv_data);
1368+ }
1369+ }
13111370} // namespace __private__
13121371
13131372// Macros
1314-
13151373#define FINE_NIF (name, flags ) \
13161374 static ERL_NIF_TERM name##_nif(ErlNifEnv *env, int argc, \
13171375 const ERL_NIF_TERM argv[]) { \
@@ -1345,16 +1403,16 @@ inline int load(ErlNifEnv *env, void **, ERL_NIF_TERM) {
13451403 auto &nif_funcs = fine::__private__::get_erl_nif_funcs (); \
13461404 auto num_funcs = static_cast <int >(nif_funcs.size ()); \
13471405 auto funcs = nif_funcs.data (); \
1348- auto load = fine::__private__::load; \
1406+ \
13491407 static ErlNifEntry entry = {ERL_NIF_MAJOR_VERSION, \
13501408 ERL_NIF_MINOR_VERSION, \
13511409 name, \
13521410 num_funcs, \
13531411 funcs, \
1354- load, \
1355- NULL , \
1412+ fine::__private__::load, \
13561413 NULL , \
13571414 NULL , \
1415+ fine::__private__::unload, \
13581416 ERL_NIF_VM_VARIANT, \
13591417 1 , \
13601418 sizeof (ErlNifResourceTypeInit), \
0 commit comments