Skip to content

Conversation

brodeuralexis
Copy link
Contributor

It is sometimes necessary for a NIF to have some global initialization logic in its load callback, like we already have for atom and resource registration.

This commit inlines the fine::__private__::load function in the FINE_INIT macro to delay template instantiation of a new fine::__private__::OnLoad struct that can be overriden by a user using the FINE_LOAD(env) macro to provide customized initialization logic, while still registering atoms and resources.

It is sometimes necessary for a NIF to have some global initialization
logic in its load callback, like we already have for atom and resource
registration.

This commit inlines the `fine::__private__::load` function in the
`FINE_INIT` macro to delay template instantiation of a new
`fine::__private__::OnLoad` struct that can be overriden by a user using
the `FINE_LOAD(env)` macro to provide customized initialization logic,
while still registering atoms and resources.
Copy link
Member

@jonatanklosko jonatanklosko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @brodeuralexis! I dropped a comment regarding the API :)

That said, I wonder if there is a logic that the user would put on load, which couldn't be a typical initialization. In the docs example, couldn't they could as well do this:

static ThreadPool s_pool = ThreadPool(std::thread::hardware_concurrency());

And if that's the case, I would probably hold off with extra APIs, until there is a use case.

Comment on lines +612 to +614
Callbacks MUST be used before `FINE_NIF`, since the formers use template
specializations that are instantiated by the latter. If the order is reverse,
there is no guarantee that `FINE_NIF` will actually invoke the callbacks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FINE_NIF -> FINE_INIT. Also I would skip the explanation, since it's just implementation details.

Suggested change
Callbacks MUST be used before `FINE_NIF`, since the formers use template
specializations that are instantiated by the latter. If the order is reverse,
there is no guarantee that `FINE_NIF` will actually invoke the callbacks.
Callbacks MUST be used before `FINE_INIT`, otherwise there is no guarantee
that `FINE_NIF` will actually invoke the callbacks.

Comment on lines +625 to +627
FINE_LOAD(env) {
s_pool = ThreadPool(std::thread::hardware_concurrency());
}
Copy link
Member

@jonatanklosko jonatanklosko Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think macros should be self-contained, in the sense that calling FOO(bar) generates fully valid code, otherwise the syntax can be confusing (and I expect errors too). For consistency we also enforce semicolons after FINE_* registrations by adding static_assert(true, "require a semicolon after the macro") at the end of the generated code.

So, even though it's slightly less convenient, I would rather do this:

void load(ErlNifEnv* caller_env, void** priv_data, ERL_NIF_TERM load_info) {
  // ...
}

FINE_LOAD(load);

We likely should expose all the arguments that the erlang callback has.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants