erl_nif のメモ（追記あり）


以前[試した際](https://github.com/tomaon/embstring)、使い方がわからず放置した[enif_open_resource_type/6](http://www.erlang.org/doc/man/erl_nif.html#enif_open_resource_type)を[eleveldb](https://github.com/basho/eleveldb/blob/develop/c_src/workitems.cc)に発見、試してみた。

でも、、なにかおかしい。

```erlang:nif.erl
-module(nif).

%% -- public --
-export([on_load/0]).
-export([open/1, close/1]).

%% == public ==

-on_load(on_load/0).

-spec on_load() -> ok|{error,_}.
on_load() ->
    Path = filename:join([lib_dir(?MODULE), ?MODULE_STRING]),
    LoadInfo = [],
    erlang:load_nif(Path, LoadInfo).


-spec open(integer()) -> {ok,binary()}|{error,_}.
open(Id)
  when is_integer(Id) ->
    erlang:nif_error(not_loaded).

-spec close(binary()) -> ok|{error,_}.
close(Resource)
  when is_binary(Resource) ->
    erlang:nif_error(not_loaded).

%% == private ==

lib_dir(Application) ->
    filename:join(lib_dir(Application,priv), "lib").

lib_dir(Application, SubDir) ->
    case code:lib_dir(Application, SubDir) of
        {error, bad_name} ->
            {ok, Dir} = file:get_cwd(),
            filename:join(Dir, atom_to_list(SubDir));
        Dir ->
            Dir
    end.
```

```cpp:nif.c
#include <stdio.h>
#include <errno.h>

#include "erl_nif.h"

#define UNUSED(p) (void)(p)

// -- --

typedef struct {
  int id;
} nif_t;

// -- --

static ERL_NIF_TERM open(ErlNifEnv *env, int argc, const ERL_NIF_TERM *argv) {

  ErlNifResourceType *resource_type = (ErlNifResourceType *)enif_priv_data(env);

  if (1 == argc &&
      enif_is_number(env, argv[0])) {

    int id = 0;
    enif_get_int(env, argv[0], &id);

    nif_t *resource = (nif_t *)enif_alloc_resource(resource_type, sizeof(nif_t));

    if (NULL != resource) {

      resource->id = id;

      printf("open, resource=%ld, id=%d\r\n", (long)resource, resource->id);

      return enif_make_tuple2(env,
                              enif_make_atom(env, "ok"),
                              enif_make_resource(env, resource));
    } else {

      return enif_make_tuple2(env,
                              enif_make_atom(env, "error"),
                              enif_make_string(env, "enomem", ERL_NIF_LATIN1));
    }
  }

  return enif_make_badarg(env);
}

static ERL_NIF_TERM close(ErlNifEnv *env, int argc, const ERL_NIF_TERM *argv) {

  ErlNifResourceType *resource_type = (ErlNifResourceType *)enif_priv_data(env);

  if (1 == argc) {

    nif_t *resource = NULL;

    if (enif_get_resource(env, argv[0], resource_type, (void **)&resource)) {

      printf("close, resource=%ld, id=%d\r\n", (long)resource, resource->id);

      enif_release_resource(resource);
      resource = NULL;

      return enif_make_atom(env, "ok");
    }
  }

  return enif_make_badarg(env);
}

// -- --

void dtor(ErlNifEnv* env, void* obj) {

  UNUSED(env);

  nif_t *resource = (nif_t *)obj;

  printf("dtor, resource=%ld, id=%d\r\n", (long)resource, resource->id);
}

// -- --

static ErlNifFunc funcs[] = {
  {"open", 1, open},
  {"close", 1, close},
};

static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {

  UNUSED(env), UNUSED(load_info);

  ErlNifResourceType *resource_type =
    enif_open_resource_type(env, NULL, "nif_t", dtor, ERL_NIF_RT_CREATE, NULL);

  if (NULL == resource_type) {
    return ENOMEM;
  }

  *priv_data = resource_type;

  return 0;
}

/*
 */
ERL_NIF_INIT(nif, funcs, load, NULL, NULL, NULL);
```

```erlang
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V5.10.4  (abort with ^G)
1> {ok,B1} = nif:open(123).
open, resource=487329320, id=123
{ok,<<>>}
2> nif:close(B1).
close, resource=487329320, id=123
ok
3> {ok,B2} = nif:open(456).
open, resource=487326352, id=456
{ok,<<>>}
4> nif:close(B2).
close, resource=487326352, id=456
ok
5> garbage_collect().
true
6> q(). 
ok
7> dtor, resource=487326352, id=456
dtor, resource=487329320, id=123
```
[enif_make_resource/2](http://www.erlang.org/doc/man/erl_nif.html#enif_make_resource)してしまうと dtor は呼ばれない。
nif を unload する仕組みはない？から、、


どうすればいいのやら。


- - -

追記： shell での binding を忘れてました。。 

```erlang
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V5.10.4  (abort with ^G)
1> F = fun() -> {ok,B} = nif:open(123), nif:close(B) end,
1> F().
open, resource=520883752, id=123
close, resource=520883752, id=123
ok
2> erlang:garbage_collect().
dtor, resource=520883752, id=123
true
3> q().
ok

```