added the second proposal
authorWolfgang Thaller <wolfgang.thaller@gmx.net>
Sun, 5 Jan 2003 17:40:24 +0000 (17:40 +0000)
committerWolfgang Thaller <wolfgang.thaller@gmx.net>
Sun, 5 Jan 2003 17:40:24 +0000 (17:40 +0000)
ffi/threads.txt

index 46859ed..f7d3f9d 100644 (file)
@@ -50,6 +50,10 @@ create Haskell threads so there are four cases to consider:
 
      foreign export bound foo :: <type>
 
+   or
+     
+     foreign import "bound wrapper" wrapFoo :: <type>
+
    and otherwise behave like normal foreign exports.
 
 4) ToDo: For completeness, there ought to be a way to 'bind'
@@ -98,4 +102,179 @@ context switch for the same reason.
 
 
 End of Proposal 1
-=================
\ No newline at end of file
+=================
+
+
+Proposal 2
+==========
+
+Goals
+~~~~~
+
+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 native thread and that if an
+external library calls into Haskell, then any outgoing calls from
+Haskell are performed by the same native thread.
+
+This specification is intended to be implementable both by
+multithreaded Haskell implementations and by single-threaded
+implementations and so it does not comment on which particular OS
+thread is used to execute Haskell code.
+
+Definitions
+~~~~~~~~~~~
+
+A native thread is a thread as defined by the operating system.
+
+A "Haskell thread" encapsulates the execution of a Haskell I/O action. A
+Haskell thread is created by forkIO, and dies when the I/O action
+completes. When a Haskell thread calls a foreign imported function, it
+is considered to be 'blocked' while the foreign function is executing.
+If the foreign imported function calls back to Haskell, any IO actions
+it triggers are considered to be executed in new Haskell threads. This
+is the way it is implemented in GHC. Other implementations may implement
+it differently, but it is believed that the proposal can be implemented
+without following the GHC way of doing things here. The only observable
+difference should be the behaviour of myThreadID in conjunction with
+foreign functions.
+
+Design
+~~~~~~
+
+1) Haskell threads may be associated at thread creation time with either
+zero or one native threads. Each Native thread is associated with zero
+or more Haskell threads.
+
+2) A Haskell thread is always executed by a native thread. This
+specification places absolutely no restrictions on which native thread
+is used to execute a particular Haskell thread. The Haskell thread need
+not be associated with the native thread used to execute it, and one
+Haskell thread may be executed by more than one native thread during its
+lifetime [but not by several native threads at once].
+
+3) If a native thread is associated with one or more Haskell threads,
+then at most one of the bound Haskell threads must be runnable at any
+time. All but at most one of the bound Haskell threads must be blocked
+on a foreign function call.
+
+4) The thread that main runs in, threads created using forkIO and
+threads created for running finalizers or signal handlers are not
+necessarily associated with a native thread. However, an implementation
+might choose to do so.
+
+5) There are now two kinds of foreign exported [and foreign import
+wrapped] functions: bound and free. The FFI syntax should be extended
+appropriately:
+
+Bound foreign exports have the form:
+
+foreign export bound foo :: <type>
+
+And bound foreign wrappers should be declared as follows:
+
+foreign import "bound wrapper" wrapFoo :: <type>
+
+All other foreign exports and wrappers are considered as free (unbound).
+
+6) When a foreign imported function is invoked [by Haskell code], the
+foreign code is executed in the native thread associated with the
+current Haskell thread, if an association exists. If the current Haskell
+thread is not associated to a native thread, the implementation may
+freely decide which thread to run the foreign function in. The existing
+distinction between unsafe, safe and threadsafe calls remains unchanged.
+
+7) When a "bound" foreign exported function is invoked [by foreign
+code], a new Haskell thread is created and associated with the native
+thread. The new associated Haskell thread is then used to execute the
+callback. The invariants stated in 3) automatically hold, because the
+foreign code must either run in a native thread that is not bound, or it
+must have been called by haskell code bound to that thread (which means
+that the other bound haskell threads are blocked on the foreign call).
+
+8) When a "free" foreign exported function is invoked, the
+implementation may freely choose what kind of Haskell thread the
+function is executed in. It is not specified whether this thread is
+associated with a particular OS thread or not.
+
+9) A new library routine, forkNativeThread :: IO () -> IO ThreadID,
+should spawn a new Haskell Thread (like forkIO) and associate it with a
+new native thread (forkIO is not guaranteed to do this). It may be
+implemented using the FFI and an OS-specific thread creation routine. It
+would just pass a "bound" callback as an entry point for a new OS
+thread.
+
+Issues
+~~~~~~
+
+Finalizers and signal handlers cannot be associated with a particular
+native thread. If they have to trigger an action in a particular native
+thread, a message has to be sent manually (via MVars and friends) to the
+Haskell thread associated with the native thread in question.
+
+The term "Haskell thread" is confusing. The current proposal is strongly
+influenced by the GHC-way of seeing things. It doesn't really matter
+whether a callback is executed in a new Haskell thread or in the Haskell
+thread that called out of Haskell Land in the first place [if such a
+thread exists].
+
+Implementations
+~~~~~~~~~~~~~~~
+
+Here are some examples of how the specification might be implemented.
+They should not be considered an actual part of the specification.
+
+1)
+Let's assume we have a haskell system that has used OS native threads
+from the start. Every call to forkIO creates a new OS thread. The OS is
+responsible for all scheduling. Now we want to add support for [my
+version of] the proposal to this implementation.
+This should be trivial to do: A foreign call should be just a call, and
+a callback should just start executing Haskell code in the current OS
+thread.
+This implementation would treat all foreign exports as bound ("the
+implementation may freely choose what kind of Haskell thread the
+function is executed in"). All "safe" calls will probably be treated as
+"threadsafe" (after all, it's no use blocking other threads). 
+If it weren't for the performance problems, this would be the ideal
+solution for me.
+
+2)
+Let's assume we have a haskell system that executes all Haskell code in
+one thread and does its own scheduling between those threads. Now we
+want to add support for [my version of] the proposal. We do not want to
+move execution of Haskell code to different threads. We are not
+concerned about performance.
+In this case, we would keep track of the association between Haskell
+threads and "foreign" OS threads (here, the term "foreign thread" seems
+to fit very well). If the Haskell code calls a foreign imported
+function, a message is sent to the associated foreign thread (a new
+foreign thread is created if necessary). If a foreign exported function
+is called, it just signals the "Haskell runtime thread".
+The performance would be better than 1) as long as no foreign functions
+are involved. When the ffi is used, performance gets worse.
+
+3)
+"The Middle Way", i.e. what I think should be implemented for GHC. The
+following are just fragments of thoughts, don't expect it to be complete
+yet:
+* There is a global lock [that's the Capability in the GHC RTS]
+which prevents several haskell threads from running truly concurrently.
+* Each bound Haskell thread is executed by its associated native thread.
+* Each bound native thread is executing at most one piece of code at a
+time, i.e. there is no scheduling going on inside the bound native
+thread.
+* When a bound foreign export is invoked, the RTS creates a new Haskell
+thread bound to the current OS thread.
+The following things are unchanged:
+* Unsafe calls are just plain old function calls
+* All unbound Haskell threads are executed by a so-called "worker
+thread". When an unbound Haskell thread calls a threadsafe imported
+function, a new worker thread is created.
+* when an unbound foreign export is invoked, the RTS creates a new
+unbound Haskell thread.
+
+End of Proposal 2
+=================