以前試した際、使い方がわからず放置したenif_open_resource_type/6をeleveldbに発見、試してみた。
でも、、なにかおかしい。
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.
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 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してしまうと dtor は呼ばれない。
nif を unload する仕組みはない?から、、
どうすればいいのやら。
追記: shell での binding を忘れてました。。
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