cerlangerlang-niferlang-driver

Enif_send function from created thread issue


I tried to invoke enif_send from a created thread. Mainly, I have an erlang process which listen to a received message emitted from my nif.

Firstly, in my enif function I got the pid of my erlang process and I cast it, then I call enif_send to send a message as follows:

ErlNifPid* pid = (ErlNifPid*) enif_alloc(sizeof(ErlNifPid));
ErlNifPid * erlang_pid = (ErlNifPid*)argv;
ERL_NIF_TERM pid1 = enif_make_pid(env , erlang_pid);

if(enif_is_pid(env,pid1))
printf(" it's pid\n");
else
printf(" it's not pid\n");

msg_env = enif_alloc_env();
ERL_NIF_TERM replay = (ERL_NIF_TERM) *text;
msg = enif_make_tuple(msg_env, replay);
if(enif_send(NULL, erlang_pid, msg_env,msg))
printf("  send succeed\n");
else
printf(" send failed\n");

Until this stage I am able to send a message to my erlang process.

After that I spawned an enif_thread and I passed to it env and pid as parameters, the I tried to call enif_send from my thread like:

ERL_NIF_TERM pid1 = enif_make_pid(conn->env , conn->pid);
if(enif_is_pid(conn->env,pid1))
printf("it's pid\n");
else
printf(" it's not pid\n");
for (int i=0;i<index;i++)
{
msg_env = enif_alloc_env();

msg = enif_make_int(msg_env, 1); 
 if(enif_send(conn->env, conn->pid, msg_env,msg))
printf("  send succeed\n");
else
printf(" send failed\n");

But when I checked the pid with enif_is_pid I got an invalid pid even it's the same pid checked in the function which spawns the thread, so consequently the send failed. Any idea please ?


Solution

  • As the documentation for enif_send states, the env parameter should be NULL when the function is called from a created thread.

    BTW, it's hard to tell without seeing all the code, but this part of the code you show might be problematic:

    ErlNifPid * erlang_pid = (ErlNifPid*)argv;
    ERL_NIF_TERM pid1 = enif_make_pid(env, erlang_pid);
    

    If you want the pid of the caller, you should either use the enif_self function or if the pid is passed to the NIF as an argument, you should extract it using enif_get_local_pid. For example, if it's the first argument passed from the Erlang caller:

    ErlNifPid pid;
    if (!enif_get_local_pid(env, argv[0], &pid))
        return enif_make_badarg(env);
    

    You can then copy it for use in the created sending thread.