[m-dev.] thread.spawn_native

Paul Bone paul at bone.id.au
Fri Jun 13 13:03:58 AEST 2014


On Fri, Jun 13, 2014 at 12:51:48PM +1000, Michael Day wrote:
>>> Right.  However, if such a call occurs within a parallel conjunction
>>> then it could be performed on one of the parallel worker threads
>>> instead.  I can't think of a situation where you'd write that code
>>> deliberately.
>>
>> Deliberate or not this affects software composability, so we need to make it
>> safe.
>
> So for foreign APIs that require being called on the same thread, there  
> will be two (or three?) ways to make them work in Mercury:
>
> 1) Manage your own threads by calling spawn_native and only calling the  
> foreign API from that thread.
>
> 2) Let the compiler manage the threads by putting may_not_migrate  
> annotations on the foreign procs.
>
> 3) Something to do with IO worker threads, so when you call a foreign  
> proc it transparently switches to an IO worker thread and calls it from  
> there instead?
>

These actually need to be combined to work.

1+2 work together, and 3 can be used with the others.  But essentially, if
you have a library like libxml or opengl that stores some state in thread
local storage (TLS) then you need to manage it somehow.  And depending on
your library's requirements what you do might change.

For example, OpenGL requires thet the same thread is always used with the
same context, but typical applications only have a single context, so using
a may_not_migrate annotation on all the OpenGL calls is sufficient.

However, libxml might be used with multiple documents concurrently.  And
only one thread may work on a document at a time.  This means that you will
need may_not_migrate combined with spawn_native,  or alternativly
the explicit IO worker proposal I described earlier.

Your third option, the IO worker threads, is more of an implementation
detail.  But it may be exposed via the standard library to give programmers
more flexability.

> I like option #1 the best :)

If this isn't combined with a method of pinning the context to the engine,
and specifying that any foreign calls made within parallel conjunctions are
also executed on the same context (all implementation details) then it won't
work.  Which is what may_not_migrate is intended to solve.

> I don't see how option #2 will work. If a naive wrapping of legacy  
> OpenGL made each function a foreign proc, then would each call to  
> glVertex potentially need to switch threads? Wouldn't there be a lot of  
> overhead? And doesn't option #3 suffer from this as well?

In option #2 and because glVertex doesn't block so we can ignore
asynchronous IO.  there is no need to switch threads all the time.  If the
context is on the wrong thread then it is switched to the correct one before
calling glVertex and remains there until there's a different reason to
switch it again (maybe it goes to sleep and wakes up elsewhere?).  These
context switches should be rare but will be able to be measured.

Option #3 does suffer from this for blocking calls or (maybe) reenterant
calls only.  The only reason one would switch to an IO thread is if a
blocking call has a chance of blocking all work on that Mercury engine.  Or
a call that may call Mercury code needs to return through Mercury code but
it is blocked because that engine has executed some other C code in the
meantime and so it's C stack pointer is in the wrong position.

Thanks.


-- 
Paul Bone



More information about the developers mailing list