[erlang-questions] enif_free_env crashes VM

Volodymyr Kyrychenko vladimir.kirichenko@REDACTED
Mon Nov 11 17:08:33 CET 2013


I have big data structure and need to have massively parallel access to 
it. SO i invented the wheel called references. Here is how it supposed 
to work - it is NIF copying this BDS into process independent 
environment and giving processes reference to the term in that env. 
Sometimes structure gets updated and reference supposed to be destroyed 
in NIF destructor. Everything was fine until things got real with 
massive access. VM just gets silently crushed w/o crash_dump. It was 
discovered that enif_free_env do not crashes vm itself but calling it 
crashes VM elsewhere - probably someone tries to read dereferenced 
value. Am I missing something or its a bug? R15B3 Linux.

Here is the code:
== xl_ref.c =========================================================

#include <erl_nif.h>
#include <stdio.h>

ERL_NIF_TERM xl_ref_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM 
ERL_NIF_TERM xl_ref_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM 

void xl_ref_dtor(ErlNifEnv* env, void* arg);

int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);

static ErlNifFunc nif_funcs[] = {
     {"new", 1, xl_ref_new},
     {"value", 1, xl_ref_value}

ERL_NIF_INIT(xl_ref, nif_funcs, &on_load, NULL, NULL, NULL)

typedef struct { ErlNifEnv* env; ERL_NIF_TERM value; } ref_t;
static ErlNifResourceType* XL_REF_RESOURCE;

ERL_NIF_TERM xl_ref_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM 
argv[]) {
     ERL_NIF_TERM object = argv[0];

     ref_t* ref = (ref_t*)enif_alloc_resource(XL_REF_RESOURCE, 
     ref->env = enif_alloc_env();
     ref->value = enif_make_copy(ref->env, object);
     ERL_NIF_TERM ref_term = enif_make_resource(env, ref);
     return ref_term;

ERL_NIF_TERM xl_ref_value(ErlNifEnv* env, int argc, const ERL_NIF_TERM 
argv[]) {
     ref_t* ref;
     if (enif_get_resource(env, argv[0], XL_REF_RESOURCE, (void**)&ref))
         return ref->value;
         return enif_make_badarg(env);

void xl_ref_dtor(ErlNifEnv* env, void* arg) {
     ref_t* ref = (ref_t*) arg;
     //Here is the problem

int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
     ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE 
     XL_REF_RESOURCE = enif_open_resource_type(env, NULL, 
"xl_ref_resource", &xl_ref_dtor, flags, 0);
     return 0;

== xl_ref.erl ==============================================


%% API
-export([new/1, value/1]).

-opaque(ref() :: reference()).

init() -> erlang:load_nif(find_nif(xl_stdlib, ?MODULE), 0).

-spec(new(term()) -> ref()).
new(_X) -> erlang:nif_error(nif_not_loaded).

-spec(value(ref()) -> term()).
value(_R) -> erlang:nif_error(nif_not_loaded).

find_nif(App, Module) ->
     filename:join(priv_dir(App, Module), atom_to_list(Module)).

priv_dir(App, Module) ->
     case code:priv_dir(App) of
         {error, _} ->
             EbinDir = filename:dirname(code:which(Module)),
             AppPath = filename:dirname(EbinDir),
             filename:join(AppPath, "priv");
         Path -> Path
=== xl_ref_tests.erl =========================================

-author("Volodymyr Kyrychenko <vladimir.kirichenko@REDACTED>").


ref_test() ->
     ets:new(test, [named_table, public]),
     ets:insert(test, {status, false}),
     spawn_link(fun() -> tree_refresh(20) end),
     access(self(), 100000),
         ok -> ok
     io:format(user, "done~n", []),

access(Pid, 0) -> Pid ! ok;
access(Pid, X) ->
     spawn_link(fun() ->
         [{t, Ref}] = ets:lookup(test, t),
         io:format(user, "~p\taccess start~n", [self()]),
         io:format(user, "~p\taccess done~n", [self()])
     access(Pid, X - 1).

tree_refresh(0) -> ok;
tree_refresh(X) ->
     T = lists:foldl(fun(I, Tree) ->
         gb_trees:insert(I, I, Tree)
     end, gb_trees:empty(), lists:seq(1, 10000)),
     Ref = xl_ref:new(T),
     ets:insert(test, {t, Ref}),
     tree_refresh(X - 1).

Volodymyr Kyrychenko

More information about the erlang-questions mailing list