Native Threads in the RTS
29 Nov 2002 16:29:45 +0000
> This is all getting highly confusing, as it seems we're working with
> different ideas of what's on the table. Alastair: you seem to be
> working on your own proposal - could you write it down either as a
> complete proposal or diffs to Wolfgangs?
I did. You sent comments on it and I sent back a cleaned up version.
Here it is again.
Since foreign libraries sometimes exploit thread local state, it is
necessary to provide some control over which thread is used to execute
foreign code. In particular, it is important that it should be
possible for Haskell code to arrange that a sequence of calls to a
given library are performed by the same foreign (or 'native') thread
and that if an external library calls into Haskell, then any outgoing
calls from Haskell are performed by the same foreign thread.
This specification is intended to be implementable both by
multi-threaded Haskell implementations and by single-threaded
implementations and so it does not comment on which particular OS
thread is used to execute Haskell code.
Haskell threads may be bound with either zero or one foreign threads.
Binding occurs at thread creation time. There are four ways to
create Haskell threads so there are four cases to consider:
1) forkForeignThread :: IO () -> IO ThreadId
The fresh Haskell thread is bound to a fresh foreign thread.
2) forkIO :: IO () -> IO ThreadId
The fresh Haskell thread is not bound to a foreign thread.
3) Calls to a bound foreign export allocate a fresh Haskell
thread which is then bound to the calling thread thread.
Bound foreign exports have the form
foreign export bound foo :: <type>
and otherwise behave like normal foreign exports.
4) ToDo: For completeness, there ought to be a way to 'bind'
finalizers to foreign threads but no concrete proposal currently
Calls to bound foreign imports by Haskell threads which are bound to a
foreign thread are performed by that foreign thread.
Bound foreign imports have the form
foreign import bound foo :: <type>
and otherwise behave like normal foreign imports.
Calls to any free (i.e., not bound) foreign imports may be made in
the bound thread (if it exists) or by some other foreign thread at
the implementation's discretion.
The notion of bound foreign imports could be eliminated by saying that
all foreign calls are performed by the bound thread if one exists and
eliminate the concept of 'bound foreign imports'. The only reason to
allow any flexibility is to allow for faster implementations which
perform less context switching - this is especially important for
'unsafe' foreign calls.
An alternative to forkForeignThread is to allow direct control over
bindThread :: ForeignThread -> IO ()
The current Haskell thread is bound to a fresh foreign thread.
(Any previous binding is forgotten.)
This leads to a much simpler design since it eliminates the need for
forkForeignThread and can be used for finalizers too. The cost is
that every bound foreign call requires locking and a context switch
since multiple Haskell threads may be bound to the same foreign thread
and could try to make a foreign call 'at the same time'. Binding
finalizers to foreign threads also seems to require locking and a
context switch for the same reason.