[erlang-questions] When to release resources in NIF?

Sverker Eriksson <>
Mon Mar 24 17:37:46 CET 2014


As I understand it, this is not about NIF *resources* created by 
enif_alloc_resource(),
but rather some common data structure tied to the module.

Do you access this common data via 'priv_data' or a global variable?

If you use global variable then you must handle the upgrade case when 
the *same* NIF library
is loaded by both old and new module instances. If they load the same 
library, they not only share code
but also share static data such as global variables. One way to detect 
this is with a library reference counter:

static int lib_refc = 0;   /* number of users of this dynamic library */

static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
     if (++lib_refc != 1) {
	/* should not happen */
     }
}

static int upgrade(ErlNifEnv* env, void** priv_data, void* old_priv_data, ERL_NIF_TERM load_info) {
     if (++lib_refc > 1) {
	/* This library already used by older module instance */
     }
}

static void unload(ErlNifEnv* env, void* priv_data) {
     if (--lib_refc == 0) {
	/* We are really unloading this library */
     }
     else {
	/* Library still used by newer instance of this module */
     }
}


In any case, to support module upgrade you need to decide how the old 
and new module instances should handle common data.
I guess there are three strategies:

1. No sharing. New module instance creates its own data and old instance 
destroys its own data.

2. Sharing. Old and new module instances share the same common data. 
Processes running old and new code might exist at the same time
so you need to make sure that both old and new code can access the same 
data in a safe way. You probably need some reference counting
here to make sure to not destroy the data while it's still used.

3. Stealing. The new module instance steals the data from the old 
instance. If a process calls a NIF in the old module instance
it needs to handle the fact that it has been "robbed" from its common 
data and maybe return some sort of error.


/Sverker, Erlang/OTP



On 03/24/2014 04:34 PM, Attila Rajmund Nohl wrote:

> Hello!
>
> I do have those, the problem is that I don't see any difference in the
> unload call when the module is explicitly removed using code:delete/1
> and code:purge/1 or implicitly removed during a code upgrade. In the
> first case I have to delete the locks, in the second case I don't have
> to. I've ended up with this code:
>
>      static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
>          /* create locks */
>          return 0;
>      }
>
>      static void unload(ErlNifEnv* env, void* priv_data) {
>          if (NULL != priv_data) {
>              /* delete locks */
>          }
>      }
>
>      static int upgrade(ErlNifEnv* env, void** priv_data, void**
> old_priv_data, ERL_NIF_TERM load_info) {
>          unload(env, *old_priv_data);
>          *old_priv_data = NULL;
>          load(env, priv_data, load_info);
>          return 0;
>      }
>
> It works, but I'm not sure how and why.
>
> 2014-03-24 15:17 GMT+01:00 Soup <>:
>> Theoretically, you should be able to use the "upgrade" or "reload" callbacks
>> in your NIF instead of the unload callback. It may be necessary to perform
>> the upgrade as an app/relup to use that feature, though. Perhaps someone a
>> bit more versed on NIF modules can shed some light.
>>
>> At the very least you need to specify one of those two callback functions.
>>
>> ~Soup
>>
>>
>> On Fri, Mar 21, 2014 at 1:15 PM, Attila Rajmund Nohl
>> <> wrote:
>>> Hello!
>>>
>>> I have a NIF module that uses a locks to protect a common resource.
>>> The locks are created using enif_rwlock_create in the load function
>>> and destroyed in the unload function. When I first load the Erlang
>>> module, the locks are initialized and everything is OK. When I compile
>>> the module in the same VM the first time, everything is still OK. When
>>> I compile it the second time, the old version of the module is
>>> unloaded, so unload is called. At this point I'm in trouble, because
>>> the unload function destroys the lock.
>>>
>>> I'm not quite sure how to correctly handle this situation. Somehow I
>>> should keep (or destroy/create) the locks in the upgrade step, but
>>> after the second compilation the locks are already deleted by unload.
>>> I could add a flag to the private data stating that the locks were
>>> already destroyed or not, but it feels hackish. What is the correct
>>> way?
>>> _______________________________________________
>>> erlang-questions mailing list
>>> 
>>> http://erlang.org/mailman/listinfo/erlang-questions
>>
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions
>
>




More information about the erlang-questions mailing list