Skip to content

Commit d02baeb

Browse files
committed
Reword callback to use fine::Registration
While using `fine::Registration` incurs some runtime-cost, there is no longer any constraints on the order of callback registration and `FINE_INIT`.
1 parent e3a42a9 commit d02baeb

3 files changed

Lines changed: 82 additions & 93 deletions

File tree

README.md

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -609,45 +609,33 @@ used.
609609
The Erlang NIF API allows the creation of global callbacks. This section
610610
describes how to leverage these callbacks while using Fine.
611611
612-
Callbacks MUST be used before `FINE_INIT`, since the formers use template
613-
specializations that are instantiated by the latter. If the order is reverse,
614-
there is no guarantee that `FINE_INIT` will actually invoke the callbacks.
615-
616612
### Load
617613
618614
The NIF load callback is called by the ERTS when the NIFs are being loaded by
619615
`:erlang.load_nif/2`. Fine allows customizing the behavior of the load callback
620-
using the `FINE_LOAD` macro before the `FINE_INIT` macro:
616+
using the `fine::Registration::register_load` function:
621617
622618
```c++
623619
static std::unique_ptr<ThreadPool> s_pool;
624620
625-
static void load(ErlNifEnv* caller_env, void** priv_data, fine::Term load_info)
626-
{
621+
static auto load = fine::Registration::register_load([](ErlNifEnv *env, void **priv_data, fine::Term load_info) {
627622
const auto thread_count = fine::decode<std::uint64_t>(caller_env, load_info);
628623
s_pool = std::make_unique<FixedThreadPool>(thread_count);
629-
}
630-
631-
FINE_LOAD(load);
632-
FINE_INIT("Elixir.MyLib.NIF");
624+
});
633625
```
634626
635627
### Unload
636628
637629
The NIF unload callback is called by the ERTS when the NIFs are being unloaded
638630
from the runtime. Fine allows customizing the behavior of the unload callback
639-
using the `FINE_UNLOAD` macro before the `FINE_INIT` macro:
631+
using the `fine::Registration::register_unload` function:
640632
641633
```c++
642634
static std::unique_ptr<ThreadPool> s_pool;
643635
644-
static void unload(ErlNifEnv* caller_env, void** priv_data)
645-
{
636+
static auto unload = fine::Registration::register_unload([](ErlNifEnv *env, void *priv_data) noexcept {
646637
s_pool.stop();
647-
}
648-
649-
FINE_UNLOAD(unload);
650-
FINE_INIT("Elixir.MyLib.NIF);
638+
});
651639
```
652640
653641
<!-- Docs -->

c_include/fine.hpp

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cstdint>
66
#include <cstdlib>
77
#include <cstring>
8+
#include <functional>
89
#include <map>
910
#include <memory>
1011
#include <optional>
@@ -47,6 +48,8 @@ template <typename T, typename SFINAE = void> struct Encoder;
4748

4849
namespace __private__ {
4950
std::vector<ErlNifFunc> &get_erl_nif_funcs();
51+
decltype(auto) get_erl_nif_load_callback();
52+
decltype(auto) get_erl_nif_unload_callback();
5053
void init_atoms(ErlNifEnv *env);
5154
bool init_resources(ErlNifEnv *env);
5255
} // namespace __private__
@@ -1187,6 +1190,12 @@ template <typename T> void raise(ErlNifEnv *env, const T &value) {
11871190
// Mechanism for accumulating information via static object definitions.
11881191
class Registration {
11891192
public:
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 LoadCallback register_load(LoadCallback callback) {
1214+
if (erl_nif_load_callback) {
1215+
throw std::logic_error("load callback already registered");
1216+
}
1217+
1218+
return Registration::erl_nif_load_callback = callback;
1219+
return callback;
1220+
}
1221+
1222+
// Registers an unload callback.
1223+
static UnloadCallback 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 callback;
1230+
}
1231+
12031232
private:
12041233
static bool init_resources(ErlNifEnv *env) {
12051234
for (const auto &[resource_type_ptr, name, dtor] :
@@ -1220,6 +1249,8 @@ class Registration {
12201249
}
12211250

12221251
friend std::vector<ErlNifFunc> &__private__::get_erl_nif_funcs();
1252+
friend decltype(auto) __private__::get_erl_nif_load_callback();
1253+
friend decltype(auto) __private__::get_erl_nif_unload_callback();
12231254

12241255
friend bool __private__::init_resources(ErlNifEnv *env);
12251256

@@ -1228,6 +1259,8 @@ class Registration {
12281259
resources = {};
12291260

12301261
inline static std::vector<ErlNifFunc> erl_nif_funcs = {};
1262+
inline static LoadCallback erl_nif_load_callback = {};
1263+
inline static UnloadCallback erl_nif_unload_callback = {};
12311264
};
12321265

12331266
// NIF definitions
@@ -1304,51 +1337,47 @@ inline std::vector<ErlNifFunc> &get_erl_nif_funcs() {
13041337
return Registration::erl_nif_funcs;
13051338
}
13061339

1307-
enum class CallbackStatus {
1308-
UNIMPLEMENTED,
1309-
IMPLEMENTED,
1310-
};
1340+
inline decltype(auto) get_erl_nif_load_callback() {
1341+
return Registration::erl_nif_load_callback;
1342+
}
13111343

1312-
template <CallbackStatus> struct OnLoad {
1313-
static void on_load(ErlNifEnv *, void **, ERL_NIF_TERM) {}
1314-
};
1344+
inline decltype(auto) get_erl_nif_unload_callback() {
1345+
return Registration::erl_nif_unload_callback;
1346+
}
13151347

1316-
template <CallbackStatus> struct OnUnload {
1317-
static void on_unload(ErlNifEnv *, void *) {}
1318-
};
1319-
} // namespace __private__
1348+
inline int load(ErlNifEnv *caller_env, void **priv_data,
1349+
ERL_NIF_TERM load_info) noexcept {
1350+
init_atoms(caller_env);
13201351

1321-
// Macros
1352+
if (!init_resources(caller_env)) {
1353+
return -1;
1354+
}
13221355

1323-
#define FINE_LOAD(function) \
1324-
static_assert(std::is_invocable_v<decltype((function)), ErlNifEnv *, \
1325-
void **, ERL_NIF_TERM>, \
1326-
"function must have the signature: void(*)(ErlNifEnv* " \
1327-
"caller_env, void** priv_data, fine::Term load_info)"); \
1328-
template <> \
1329-
struct fine::__private__::OnLoad< \
1330-
fine::__private__::CallbackStatus::IMPLEMENTED> { \
1331-
static void on_load(ErlNifEnv *caller_env, void **priv_data, \
1332-
ERL_NIF_TERM load_info) { \
1333-
(function)(caller_env, priv_data, load_info); \
1334-
} \
1335-
}; \
1336-
static_assert(true, "require a semicolon after the macro")
1356+
try {
1357+
if (fine::__private__::get_erl_nif_load_callback()) {
1358+
std::invoke(fine::__private__::get_erl_nif_load_callback(), caller_env,
1359+
priv_data, load_info);
1360+
}
1361+
} catch (const std::exception &e) {
1362+
enif_fprintf(stderr, "unhandled exception: %s\n", e.what());
1363+
return -1;
1364+
} catch (...) {
1365+
enif_fprintf(stderr, "unhandled throw\n");
1366+
return -1;
1367+
}
13371368

1338-
#define FINE_UNLOAD(function) \
1339-
static_assert( \
1340-
std::is_invocable_v<decltype((function)), ErlNifEnv *, void *>, \
1341-
"function must have the signature: void(*)(ErlNifEnv* " \
1342-
"caller_env, void* priv_data)"); \
1343-
template <> \
1344-
struct fine::__private__::OnUnload< \
1345-
fine::__private__::CallbackStatus::IMPLEMENTED> { \
1346-
static void on_unload(ErlNifEnv *caller_env, void *priv_data) { \
1347-
(function)(caller_env, priv_data); \
1348-
} \
1349-
}; \
1350-
static_assert(true, "require a semicolon after the macro")
1369+
return 0;
1370+
}
13511371

1372+
inline void unload(ErlNifEnv *caller_env, void *priv_data) noexcept {
1373+
if (fine::__private__::get_erl_nif_unload_callback()) {
1374+
std::invoke(fine::__private__::get_erl_nif_unload_callback(), caller_env,
1375+
priv_data);
1376+
}
1377+
}
1378+
} // namespace __private__
1379+
1380+
// Macros
13521381
#define FINE_NIF(name, flags) \
13531382
static ERL_NIF_TERM name##_nif(ErlNifEnv *env, int argc, \
13541383
const ERL_NIF_TERM argv[]) { \
@@ -1383,44 +1412,15 @@ template <CallbackStatus> struct OnUnload {
13831412
auto num_funcs = static_cast<int>(nif_funcs.size()); \
13841413
auto funcs = nif_funcs.data(); \
13851414
\
1386-
const auto load = [](ErlNifEnv *caller_env, void **priv_data, \
1387-
ERL_NIF_TERM load_info) noexcept { \
1388-
fine::__private__::init_atoms(caller_env); \
1389-
\
1390-
if (!fine::__private__::init_resources(caller_env)) { \
1391-
return -1; \
1392-
} \
1393-
\
1394-
try { \
1395-
fine::__private__:: \
1396-
OnLoad<fine::__private__::CallbackStatus::IMPLEMENTED>::on_load( \
1397-
caller_env, priv_data, load_info); \
1398-
} catch (const std::exception &e) { \
1399-
enif_fprintf(stderr, "unhandled exception: %s\n", e.what()); \
1400-
return -1; \
1401-
} catch (...) { \
1402-
enif_fprintf(stderr, "unhandled throw\n"); \
1403-
return -1; \
1404-
} \
1405-
\
1406-
return 0; \
1407-
}; \
1408-
\
1409-
const auto unload = [](ErlNifEnv *caller_env, void *priv_data) noexcept { \
1410-
fine::__private__:: \
1411-
OnUnload<fine::__private__::CallbackStatus::IMPLEMENTED>::on_unload( \
1412-
caller_env, priv_data); \
1413-
}; \
1414-
\
14151415
static ErlNifEntry entry = {ERL_NIF_MAJOR_VERSION, \
14161416
ERL_NIF_MINOR_VERSION, \
14171417
name, \
14181418
num_funcs, \
14191419
funcs, \
1420-
load, \
1420+
fine::__private__::load, \
14211421
NULL, \
14221422
NULL, \
1423-
unload, \
1423+
fine::__private__::unload, \
14241424
ERL_NIF_VM_VARIANT, \
14251425
1, \
14261426
sizeof(ErlNifResourceTypeInit), \

test/c_src/finest.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -463,14 +463,15 @@ std::uint64_t hash_atom(ErlNifEnv *, fine::Atom atom) noexcept {
463463
FINE_NIF(hash_atom, 0);
464464

465465
static bool s_loaded = false;
466-
static void load(ErlNifEnv *, void **, fine::Term) { s_loaded = true; }
466+
467+
static auto load = fine::Registration::register_load(
468+
[](ErlNifEnv *, void **, ERL_NIF_TERM) { s_loaded = true; });
469+
470+
static auto unload = fine::Registration::register_unload(
471+
[](ErlNifEnv *, void *) noexcept { s_loaded = false; });
467472

468473
bool is_loaded(ErlNifEnv *) { return s_loaded; }
469474
FINE_NIF(is_loaded, 0);
470-
471-
static void unload(ErlNifEnv *, void *) {}
472475
} // namespace finest
473476

474-
FINE_LOAD(finest::load);
475-
FINE_UNLOAD(finest::unload);
476477
FINE_INIT("Elixir.Finest.NIF");

0 commit comments

Comments
 (0)