enif_send rules questions

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

enif_send rules questions

Jason Orendorff
Hi all,

The documentation for enif_send says this:

    env
        The environment of the calling process. Must be NULL only if
        calling from a created thread.

I have a few questions about this.

1.  What is "the environment of the calling process"? Does that mean the
    environment that was passed to the NIF?

2.  What is a "created thread"?

3.  Is it really invalid to do this from a NIF call -- unless I spawn a
    separate thread to do it, and then it's OK?

        NIF_ENV msg_env = enif_alloc_env();
        NIF_TERM msg = enif_make_int(env, 1);
        enif_send(NULL, &pid, msg_env, msg);
        enif_free_env(msg_env);

4.  We suspect it's actually totally safe to pass a process-independent
    environment as the first parameter, regardless of whether there's a
    "calling process" or we're "calling from a created thread". Is it?
    If so, would you accept a patch to document that that's OK?

Background: I'm working on Rustler, a library for writing Erlang NIFs in
the Rust programming language. Rustler aims to be safe: Rustler NIFs
can't trigger undefined behavior or crash the VM, as long as you avoid
Rust's `unsafe` keyword. If you manage to crash, it's not your bug. It's
ours.

This means Rustler must enforce all "erl_nif.h" API usage rules.

We can enforce most rules with zero overhead. \o/ In a few cases, we add
a runtime check. But `enif_send` is different.  We have to know whether
or not we're in a "created thread". If not, we have to know "the
environment of the calling process", because passing any other
environment would be illegal. That imposes a bit of overhead on every
NIF call -- on entry, we have to stash the env in thread-local storage.

I measured, and on Linux, it costs about 133ns per NIF call. Not awful,
but we'd have to impose that on everyone, whether they use `enif_send`
or not. So I thought I'd ask. The changes proposed in question #4 would
eliminate the need for this overhead, because all environments would
always be OK for sending.

--
Thanks for reading!
Jason Orendorff


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: enif_send rules questions

Sverker Eriksson-4

On 01/12/2017 08:21 PM, Jason Orendorff wrote:

> Hi all,
>
> The documentation for enif_send says this:
>
>      env
>          The environment of the calling process. Must be NULL only if
>          calling from a created thread.
>
> I have a few questions about this.
>
> 1.  What is "the environment of the calling process"? Does that mean the
>      environment that was passed to the NIF?
Yes.

> 2.  What is a "created thread"?
Any non-scheduler thread where you don't have "the environment of the
calling process".
Typically a thread created with enif_thread_create().

> 3.  Is it really invalid to do this from a NIF call -- unless I spawn a
>      separate thread to do it, and then it's OK?
>
>          NIF_ENV msg_env = enif_alloc_env();
>          NIF_TERM msg = enif_make_int(env, 1);
>          enif_send(NULL, &pid, msg_env, msg);
>          enif_free_env(msg_env);
Yes.

Documentation contributions to make thing more clear are always welcome.

> 4.  We suspect it's actually totally safe to pass a process-independent
>      environment as the first parameter, regardless of whether there's a
>      "calling process" or we're "calling from a created thread". Is it?
>      If so, would you accept a patch to document that that's OK?
I'm not super excited about such a guarantee, at least not right now.

Even if you could conclude that it will work with current implementation,
the more guarantees we give, the harder it gets to do future
changes and optimizations.

Also master branch is a moving target in this area right now as we're
in the process of making the dirty scheduler feature more robust for OTP 20.


/Sverker, Erlang/OTP

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: enif_send rules questions

Jason Orendorff
On Fri, Jan 13, 2017 at 10:09 AM, Sverker Eriksson <[hidden email]> wrote:
On 01/12/2017 08:21 PM, Jason Orendorff wrote:
4.  We suspect it's actually totally safe to pass a process-independent
     environment as the first parameter, regardless of whether there's a
     "calling process" or we're "calling from a created thread". Is it?
     If so, would you accept a patch to document that that's OK?
I'm not super excited about such a guarantee, at least not right now.

Thank you for the quick, thoughtful response. I appreciate your time.

Is there a way to enforce this rule for our users that we've missed? When
our `send` method is called, I think we have to figure out if there's a current
calling process, and if so, its environment. I don't see a way to do it without
storing that information in TLS ahead of time -- at a cost to every NIF call.

We could make code that runs on created threads statically different from
code that runs in the Erlang scheduler's threads, e.g. by making
`NifCallEnv`, `AllocatedEnvInNifThread`, and `AllocatedEnvInCreatedThread`
three different types, of which only 2 have `.send()` methods. But this
seems horrible.

We have considered several possible approaches, all about as bad as that.

Thanks,
-j


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: enif_send rules questions

Daniel Goertzen-3
I see another scenario here:  What happens in the context of enif_send() in a scoped thread?  That is, in my NIF function I spin up a thread that does an enif_send() and exits before my NIF function returns.  The process environment is in fact in scope for the enif_send, but I'm also in a created thread.  Should I plug in the process environment or null?

On Sat, Jan 14, 2017 at 1:04 AM Jason Orendorff <[hidden email]> wrote:
On Fri, Jan 13, 2017 at 10:09 AM, Sverker Eriksson <[hidden email]> wrote:
On 01/12/2017 08:21 PM, Jason Orendorff wrote:
4.  We suspect it's actually totally safe to pass a process-independent
     environment as the first parameter, regardless of whether there's a
     "calling process" or we're "calling from a created thread". Is it?
     If so, would you accept a patch to document that that's OK?
I'm not super excited about such a guarantee, at least not right now.

Thank you for the quick, thoughtful response. I appreciate your time.

Is there a way to enforce this rule for our users that we've missed? When
our `send` method is called, I think we have to figure out if there's a current
calling process, and if so, its environment. I don't see a way to do it without
storing that information in TLS ahead of time -- at a cost to every NIF call.

We could make code that runs on created threads statically different from
code that runs in the Erlang scheduler's threads, e.g. by making
`NifCallEnv`, `AllocatedEnvInNifThread`, and `AllocatedEnvInCreatedThread`
three different types, of which only 2 have `.send()` methods. But this
seems horrible.

We have considered several possible approaches, all about as bad as that.

Thanks,
-j

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: enif_send rules questions

Sverker Eriksson-4
The lifetime of the thread doesn't matter.


"env    The environment of the calling process. Must be NULL only if
calling from a created thread."

enif_send(env, ...) uses argument 'env' to make assumptions about
the thread context, such as seized locks for example. When calling
from an Erlang process, the main lock of the process is held.

/Sverker


On 01/25/2017 05:14 PM, Daniel Goertzen wrote:

> I see another scenario here:  What happens in the context of enif_send() in
> a scoped thread?  That is, in my NIF function I spin up a thread that does
> an enif_send() and exits before my NIF function returns.  The process
> environment is in fact in scope for the enif_send, but I'm also in a
> created thread.  Should I plug in the process environment or null?
>
> On Sat, Jan 14, 2017 at 1:04 AM Jason Orendorff <[hidden email]>
> wrote:
>
>> On Fri, Jan 13, 2017 at 10:09 AM, Sverker Eriksson <
>> [hidden email]> wrote:
>>
>> On 01/12/2017 08:21 PM, Jason Orendorff wrote:
>>
>> 4.  We suspect it's actually totally safe to pass a process-independent
>>       environment as the first parameter, regardless of whether there's a
>>       "calling process" or we're "calling from a created thread". Is it?
>>       If so, would you accept a patch to document that that's OK?
>>
>> I'm not super excited about such a guarantee, at least not right now.
>>
>>
>> Thank you for the quick, thoughtful response. I appreciate your time.
>>
>> Is there a way to enforce this rule for our users that we've missed? When
>> our `send` method is called, I think we have to figure out if there's a
>> current
>> calling process, and if so, its environment. I don't see a way to do it
>> without
>> storing that information in TLS ahead of time -- at a cost to every NIF
>> call.
>>
>> We could make code that runs on created threads statically different from
>> code that runs in the Erlang scheduler's threads, e.g. by making
>> `NifCallEnv`, `AllocatedEnvInNifThread`, and `AllocatedEnvInCreatedThread`
>> three different types, of which only 2 have `.send()` methods. But this
>> seems horrible.
>>
>> We have considered several possible approaches, all about as bad as that.
>>
>> Thanks,
>> -j
>>
>> _______________________________________________
>> erlang-questions mailing list
>> [hidden email]
>> http://erlang.org/mailman/listinfo/erlang-questions
>>

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: enif_send rules questions

Jason Orendorff
Quick follow-up: I found out I could use enif_thread_type() to detect cases where my users would be breaking the documented contract, and error out instead.

-j

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Loading...