added the second proposal
[haskell-report.git] / ffi / threads.txt
1 Foreign Threads
2 ===============
3
4 There are two variant attempts to address issues associated with
5 thread-local state in foreign threads (i.e., those provided by the
6 operating system or standard libraries as opposed to those provided by
7 a Haskell runtime system).
8
9 Until such time as we are able to merge the proposals, we include both
10 proposals as self-contained entities.
11
12
13 Proposal 1
14 ==========
15
16 Goals
17 ~~~~~
18
19 Since foreign libraries sometimes exploit thread local state, it is
20 necessary to provide some control over which thread is used to execute
21 foreign code.  In particular, it is important that it should be
22 possible for Haskell code to arrange that a sequence of calls to a
23 given library are performed by the same foreign (or 'native') thread
24 and that if an external library calls into Haskell, then any outgoing
25 calls from Haskell are performed by the same foreign thread.
26
27 This specification is intended to be implementable both by
28 multi-threaded Haskell implementations and by single-threaded
29 implementations and so it does not comment on which particular OS
30 thread is used to execute Haskell code.
31
32
33 Design
34 ~~~~~~
35
36 Haskell threads may be bound with either zero or one foreign threads.
37 Binding occurs at thread creation time.  There are four ways to
38 create Haskell threads so there are four cases to consider:
39
40 1) forkForeignThread :: IO () -> IO ThreadId
41    The fresh Haskell thread is bound to a fresh foreign thread.
42
43 2) forkIO :: IO () -> IO ThreadId
44    The fresh Haskell thread is not bound to a foreign thread.
45
46 3) Calls to a bound foreign export allocate a fresh Haskell
47    thread which is then bound to the calling thread thread.
48
49    Bound foreign exports have the form
50
51      foreign export bound foo :: <type>
52
53    or
54      
55      foreign import "bound wrapper" wrapFoo :: <type>
56
57    and otherwise behave like normal foreign exports.
58
59 4) ToDo: For completeness, there ought to be a way to 'bind'
60    finalizers to foreign threads but no concrete proposal currently
61    exists.
62
63 Calls to bound foreign imports by Haskell threads which are bound to a
64 foreign thread are performed by that foreign thread.
65
66    Bound foreign imports have the form
67
68      foreign import bound foo :: <type>
69
70    and otherwise behave like normal foreign imports.
71
72 Calls to any free (i.e., not bound) foreign imports may be made in
73 the bound thread (if it exists) or by some other foreign thread at
74 the implementation's discretion.
75
76
77 Issues
78 ~~~~~~
79
80 The notion of bound foreign imports could be eliminated by saying that
81 all foreign calls are performed by the bound thread if one exists and
82 eliminate the concept of 'bound foreign imports'.  The only reason to
83 allow any flexibility is to allow for faster implementations which
84 perform less context switching - this is especially important for
85 'unsafe' foreign calls.
86
87
88 An alternative to forkForeignThread is to allow direct control over
89 thread binding:
90
91    bindThread :: ForeignThread -> IO ()
92    The current Haskell thread is bound to a fresh foreign thread.
93    (Any previous binding is forgotten.)
94
95 This leads to a much simpler design since it eliminates the need for
96 forkForeignThread and can be used for finalizers too.  The cost is
97 that every bound foreign call requires locking and a context switch
98 since multiple Haskell threads may be bound to the same foreign thread
99 and could try to make a foreign call 'at the same time'.  Binding
100 finalizers to foreign threads also seems to require locking and a
101 context switch for the same reason.
102
103
104 End of Proposal 1
105 =================
106
107
108 Proposal 2
109 ==========
110
111 Goals
112 ~~~~~
113
114 Since foreign libraries sometimes exploit thread local state, it is
115 necessary to provide some control over which thread is used to execute
116 foreign code.  In particular, it is important that it should be
117 possible for Haskell code to arrange that a sequence of calls to a
118 given library are performed by the same native thread and that if an
119 external library calls into Haskell, then any outgoing calls from
120 Haskell are performed by the same native thread.
121
122 This specification is intended to be implementable both by
123 multithreaded Haskell implementations and by single-threaded
124 implementations and so it does not comment on which particular OS
125 thread is used to execute Haskell code.
126
127 Definitions
128 ~~~~~~~~~~~
129
130 A native thread is a thread as defined by the operating system.
131
132 A "Haskell thread" encapsulates the execution of a Haskell I/O action. A
133 Haskell thread is created by forkIO, and dies when the I/O action
134 completes. When a Haskell thread calls a foreign imported function, it
135 is considered to be 'blocked' while the foreign function is executing.
136 If the foreign imported function calls back to Haskell, any IO actions
137 it triggers are considered to be executed in new Haskell threads. This
138 is the way it is implemented in GHC. Other implementations may implement
139 it differently, but it is believed that the proposal can be implemented
140 without following the GHC way of doing things here. The only observable
141 difference should be the behaviour of myThreadID in conjunction with
142 foreign functions.
143
144 Design
145 ~~~~~~
146
147 1) Haskell threads may be associated at thread creation time with either
148 zero or one native threads. Each Native thread is associated with zero
149 or more Haskell threads.
150
151 2) A Haskell thread is always executed by a native thread. This
152 specification places absolutely no restrictions on which native thread
153 is used to execute a particular Haskell thread. The Haskell thread need
154 not be associated with the native thread used to execute it, and one
155 Haskell thread may be executed by more than one native thread during its
156 lifetime [but not by several native threads at once].
157
158 3) If a native thread is associated with one or more Haskell threads,
159 then at most one of the bound Haskell threads must be runnable at any
160 time. All but at most one of the bound Haskell threads must be blocked
161 on a foreign function call.
162
163 4) The thread that main runs in, threads created using forkIO and
164 threads created for running finalizers or signal handlers are not
165 necessarily associated with a native thread. However, an implementation
166 might choose to do so.
167
168 5) There are now two kinds of foreign exported [and foreign import
169 wrapped] functions: bound and free. The FFI syntax should be extended
170 appropriately:
171
172 Bound foreign exports have the form:
173
174 foreign export bound foo :: <type>
175
176 And bound foreign wrappers should be declared as follows:
177
178 foreign import "bound wrapper" wrapFoo :: <type>
179
180 All other foreign exports and wrappers are considered as free (unbound).
181
182 6) When a foreign imported function is invoked [by Haskell code], the
183 foreign code is executed in the native thread associated with the
184 current Haskell thread, if an association exists. If the current Haskell
185 thread is not associated to a native thread, the implementation may
186 freely decide which thread to run the foreign function in. The existing
187 distinction between unsafe, safe and threadsafe calls remains unchanged.
188
189 7) When a "bound" foreign exported function is invoked [by foreign
190 code], a new Haskell thread is created and associated with the native
191 thread. The new associated Haskell thread is then used to execute the
192 callback. The invariants stated in 3) automatically hold, because the
193 foreign code must either run in a native thread that is not bound, or it
194 must have been called by haskell code bound to that thread (which means
195 that the other bound haskell threads are blocked on the foreign call).
196
197 8) When a "free" foreign exported function is invoked, the
198 implementation may freely choose what kind of Haskell thread the
199 function is executed in. It is not specified whether this thread is
200 associated with a particular OS thread or not.
201
202 9) A new library routine, forkNativeThread :: IO () -> IO ThreadID,
203 should spawn a new Haskell Thread (like forkIO) and associate it with a
204 new native thread (forkIO is not guaranteed to do this). It may be
205 implemented using the FFI and an OS-specific thread creation routine. It
206 would just pass a "bound" callback as an entry point for a new OS
207 thread.
208
209 Issues
210 ~~~~~~
211
212 Finalizers and signal handlers cannot be associated with a particular
213 native thread. If they have to trigger an action in a particular native
214 thread, a message has to be sent manually (via MVars and friends) to the
215 Haskell thread associated with the native thread in question.
216
217 The term "Haskell thread" is confusing. The current proposal is strongly
218 influenced by the GHC-way of seeing things. It doesn't really matter
219 whether a callback is executed in a new Haskell thread or in the Haskell
220 thread that called out of Haskell Land in the first place [if such a
221 thread exists].
222
223 Implementations
224 ~~~~~~~~~~~~~~~
225
226 Here are some examples of how the specification might be implemented.
227 They should not be considered an actual part of the specification.
228
229 1)
230 Let's assume we have a haskell system that has used OS native threads
231 from the start. Every call to forkIO creates a new OS thread. The OS is
232 responsible for all scheduling. Now we want to add support for [my
233 version of] the proposal to this implementation.
234 This should be trivial to do: A foreign call should be just a call, and
235 a callback should just start executing Haskell code in the current OS
236 thread.
237 This implementation would treat all foreign exports as bound ("the
238 implementation may freely choose what kind of Haskell thread the
239 function is executed in"). All "safe" calls will probably be treated as
240 "threadsafe" (after all, it's no use blocking other threads). 
241 If it weren't for the performance problems, this would be the ideal
242 solution for me.
243
244 2)
245 Let's assume we have a haskell system that executes all Haskell code in
246 one thread and does its own scheduling between those threads. Now we
247 want to add support for [my version of] the proposal. We do not want to
248 move execution of Haskell code to different threads. We are not
249 concerned about performance.
250 In this case, we would keep track of the association between Haskell
251 threads and "foreign" OS threads (here, the term "foreign thread" seems
252 to fit very well). If the Haskell code calls a foreign imported
253 function, a message is sent to the associated foreign thread (a new
254 foreign thread is created if necessary). If a foreign exported function
255 is called, it just signals the "Haskell runtime thread".
256 The performance would be better than 1) as long as no foreign functions
257 are involved. When the ffi is used, performance gets worse.
258
259 3)
260 "The Middle Way", i.e. what I think should be implemented for GHC. The
261 following are just fragments of thoughts, don't expect it to be complete
262 yet:
263 * There is a global lock [that's the Capability in the GHC RTS]
264 which prevents several haskell threads from running truly concurrently.
265 * Each bound Haskell thread is executed by its associated native thread.
266 * Each bound native thread is executing at most one piece of code at a
267 time, i.e. there is no scheduling going on inside the bound native
268 thread.
269 * When a bound foreign export is invoked, the RTS creates a new Haskell
270 thread bound to the current OS thread.
271 The following things are unchanged:
272 * Unsafe calls are just plain old function calls
273 * All unbound Haskell threads are executed by a so-called "worker
274 thread". When an unbound Haskell thread calls a threadsafe imported
275 function, a new worker thread is created.
276 * when an unbound foreign export is invoked, the RTS creates a new
277 unbound Haskell thread.
278
279 End of Proposal 2
280 =================