[erlang-questions] erl_nif: persist binary across calls

Sverker Eriksson sverker@REDACTED
Fri Feb 8 16:49:44 CET 2019


Hi Robert,

The 'env' you get in 'load' is temporary. Terms created in it will disappear
when 'load' returns.
Use enif_alloc_env() to create terms that will survive between NIF calls:

ErlNifEnv* persistent_env;

int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
{
    persistent_env = enif_alloc_env();
    persistent = create_binary(persistent_env);
    return 0;
}


Use enif_make_copy() to copy terms between ErlNifEnv's:

static ERL_NIF_TERM world_nif(ErlNifEnv* env, ...)
{
    return enif_make_copy(persistent);
}

If the persistent binary is large (> 64 bytes) then only a small term header
will be copied pointing to the binary data.


/Sverker

On fre, 2019-02-08 at 14:04 +0000, Harris, Robert wrote:
> Hi,
> 
> I am writing a NIF library in which I would like to create persistent binary
> terms
> in my load() callback.  The purpose of these terms is to act as ready-made
> keys
> for frequent calls to enif_get_map_value() in successive calls to other NIFs
> in the
> same library.
> 
> My test case is
> 
> > 
> > #include <string.h>
> > #include <erl_nif.h>
> > 
> > ERL_NIF_TERM persistent;
> > 
> > static ERL_NIF_TERM
> > create_binary(ErlNifEnv *env)
> > {
> >         ErlNifBinary binary;
> >         char *data = "abcdefghijklmnopqrstuvwxyz";
> >         size_t size = strlen(data);
> > 
> >         (void) enif_alloc_binary(size, &binary);
> >         memcpy(binary.data, data, size);
> >         binary.size = size;
> >         return (enif_make_binary(env, &binary));
> > }
> > 
> > int
> > load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
> > {
> >         persistent = create_binary(env);
> >         return (0);
> > }
> > 
> > static ERL_NIF_TERM
> > world_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
> > {
> >          return (persistent);
> > }
> > 
> > static ERL_NIF_TERM
> > hello_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
> > {
> >          return (create_binary(env));
> > }
> > 
> > static ErlNifFunc nif_funcs[] = {
> >         {"hello", 0, hello_nif},
> >         {"world", 0, world_nif},
> > };
> > 
> > ERL_NIF_INIT(hello, nif_funcs, &load, NULL, NULL, NULL)
> An example of the behaviour I see is
> 
> > 
> > Erlang/OTP 20 [erts-9.3.3.3] [source] [64-bit] [smp:12:12] [ds:12:12:10]
> > [async-threads:10] [hipe] [kernel-poll:false]
> > 
> > Eshell V9.3.3.3  (abort with ^G)
> > 1>
> > 1> hello:hello().
> > <<"abcdefghijklmnopqrstuvwxyz">>
> > 2> hello:world().
> > eheap_alloc: Cannot allocate 2305843009213695256 bytes of memory (of type
> > "heap_frag").
> > 
> > Crash dump is being written to: erl_crash.dump...done
> > make: *** [shell] Error 1
> I'm guessing that what I'm trying is wrong but I'd appreciate a pointer as to
> why.  I'm motivated in part by comments like the following extract from
> enif_make_new_binary():
> 
> > 
> > The drawbacks are that the binary cannot be kept between NIF calls and it
> > cannot
> > be reallocated.
> I inferred from this that a binary generated by enif_make_binary() *can* be
> kept
> between NIF calls.  I'm a newcomer to Erlang, if that's not obvious.
> 
> Regards,
> 
> Robert
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions



More information about the erlang-questions mailing list