LoginSignup
2
2

More than 5 years have passed since last update.

erl_nif のメモ(追記あり)

Last updated at Posted at 2014-02-06

以前試した際、使い方がわからず放置したenif_open_resource_type/6eleveldbに発見、試してみた。

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

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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2