让我们聊聊Erlang的nif中资源的安全释放

起因

Erlang在数学运算和字符串处理方面是比较弱,很多时候需要借助C的nif来加速。不过我们今天不讨论这个,因为今天这事情的起因是我要将Redis的AOF嵌入了Erlang。

大家都知道Redis的AOF是要打开一个文件的,但是如果我们的Erlang进程因为意外退出,nif打开的文件该怎么处理呢?因为Nif并不像Erlang的Driver那样可以监测Erlang进程的死活。

解决方法

这个时候,我想起了一个伟大的工程eleveldb。leveldb也是需要打开句柄,并且相应的进程也使用这个句柄。所以就去翻看了eleveldb的代码。发现几个非常重要的函数

ErlNifResourceType *enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried);

void *enif_alloc_resource(ErlNifResourceType* type, unsigned size);

ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj);

void enif_release_resource(void* obj);

这几个函数是怎么解决资源释放问题的呢?我们可以看到eleveldb在on_load的时候,使用enif_open_resource_type创建了一个资源类型。之后在每次请求打开DB的时候,用enif_alloc_resource分配出一个资源,然后用enif_make_resource和enif_release_resource将资源的控制权交给了调用的进程。

这么做的原理

根据阅读erts中nif相关的代码,可以看到enif_alloc_resource在分配资源的时候,分配的资源引用计数为1,并且该资源不属于任何一个Erlang的进程。

当我们调用enif_make_resource的时候,相当于在调用者的堆栈上分配了一个指向资源的指针,并将资源的引用计数加1。

接着我们再使用enif_release_resource将资源的引用计数减少1,这样就相当将资源的控制权交给了调用的Erlang进程。

那么资源是怎么安全释放的呢?这个时候我们可以看到资源的引用计数为1,并且这个引用者是Erlang进程堆。我们都知道Erlang进程崩溃后,Erts会清理Erlang进程堆和栈,释放资源。

那么当Erlang进程崩溃后,就会调用enif_open_resource_type被调用的时候所传入的析构函数,而这个析构函数的参数就是前面所分配的资源。

你可能感兴趣的:(erlang,NIF,资源释放,进程崩溃)