Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / basis-library / mlton / thread.sml
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2014 Matthew Fluet.
2 * Copyright (C) 2004-2008 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 *
5 * MLton is released under a BSD-style license.
6 * See the file MLton-LICENSE for details.
7 *)
8
9structure MLtonThread:> MLTON_THREAD_EXTRA =
10struct
11
12structure Prim = Primitive.MLton.Thread
13
14fun die (s: string): 'a =
15 (PrimitiveFFI.Stdio.print s
16 ; PrimitiveFFI.Posix.Process.exit 1
17 ; let exception DieFailed
18 in raise DieFailed
19 end)
20
21val gcState = Primitive.MLton.GCState.gcState
22
23structure AtomicState =
24 struct
25 datatype t = NonAtomic | Atomic of int
26 end
27
28local
29 open Prim
30in
31 val atomicBegin = atomicBegin
32 val atomicEnd = atomicEnd
33 val atomicState = fn () =>
34 case atomicState () of
35 0wx0 => AtomicState.NonAtomic
36 | w => AtomicState.Atomic (Word32.toInt w)
37end
38
39fun atomically f =
40 (atomicBegin (); DynamicWind.wind (f, atomicEnd))
41
42datatype 'a thread =
43 Dead
44 | Interrupted of Prim.thread
45 | New of 'a -> unit
46 (* In Paused (f, t), f is guaranteed to not raise an exception. *)
47 | Paused of ((unit -> 'a) -> unit) * Prim.thread
48
49datatype 'a t = T of 'a thread ref
50
51structure Runnable =
52 struct
53 type t = unit t
54 end
55
56fun prepend (T r: 'a t, f: 'b -> 'a): 'b t =
57 let
58 val t =
59 case !r of
60 Dead => raise Fail "prepend to a Dead thread"
61 | Interrupted _ => raise Fail "prepend to a Interrupted thread"
62 | New g => New (g o f)
63 | Paused (g, t) => Paused (fn h => g (f o h), t)
64 in r := Dead
65 ; T (ref t)
66 end
67
68fun prepare (t: 'a t, v: 'a): Runnable.t =
69 prepend (t, fn () => v)
70
71fun new f = T (ref (New f))
72
73local
74 local
75 val func: (unit -> unit) option ref = ref NONE
76 val base: Prim.preThread =
77 let
78 val () = Prim.copyCurrent ()
79 in
80 case !func of
81 NONE => Prim.savedPre gcState
82 | SOME x =>
83 (* This branch never returns. *)
84 let
85 (* Atomic 1 *)
86 val () = func := NONE
87 val () = atomicEnd ()
88 (* Atomic 0 *)
89 in
90 (x () handle e => MLtonExn.topLevelHandler e)
91 ; die "Thread didn't exit properly.\n"
92 end
93 end
94 in
95 fun newThread (f: unit -> unit) : Prim.thread =
96 let
97 (* Atomic 2 *)
98 val () = func := SOME f
99 in
100 Prim.copy base
101 end
102 end
103 val switching = ref false
104in
105 fun 'a atomicSwitch (f: 'a t -> Runnable.t): 'a =
106 (* Atomic 1 *)
107 if !switching
108 then let
109 val () = atomicEnd ()
110 (* Atomic 0 *)
111 in
112 raise Fail "nested Thread.switch"
113 end
114 else
115 let
116 val _ = switching := true
117 val r : (unit -> 'a) ref =
118 ref (fn () => die "Thread.atomicSwitch didn't set r.\n")
119 val t: 'a thread ref =
120 ref (Paused (fn x => r := x, Prim.current gcState))
121 fun fail e = (t := Dead
122 ; switching := false
123 ; atomicEnd ()
124 ; raise e)
125 val (T t': Runnable.t) = f (T t) handle e => fail e
126 val primThread =
127 case !t' before t' := Dead of
128 Dead => fail (Fail "switch to a Dead thread")
129 | Interrupted t => t
130 | New g => (atomicBegin (); newThread g)
131 | Paused (f, t) => (f (fn () => ()); t)
132 val _ = switching := false
133 (* Atomic 1 when Paused/Interrupted, Atomic 2 when New *)
134 val _ = Prim.switchTo primThread (* implicit atomicEnd() *)
135 (* Atomic 0 when resuming *)
136 in
137 !r ()
138 end
139
140 fun switch f =
141 (atomicBegin ()
142 ; atomicSwitch f)
143end
144
145fun fromPrimitive (t: Prim.thread): Runnable.t =
146 T (ref (Interrupted t))
147
148fun toPrimitive (t as T r : unit t): Prim.thread =
149 case !r of
150 Dead => die "Thread.toPrimitive saw Dead.\n"
151 | Interrupted t =>
152 (r := Dead
153 ; t)
154 | New _ =>
155 switch
156 (fn cur : Prim.thread t =>
157 prepare
158 (prepend (t, fn () =>
159 switch
160 (fn t' : unit t =>
161 prepare (cur, toPrimitive t'))),
162 ()))
163 | Paused (f, t) =>
164 (r := Dead
165 ; f (fn () => ())
166 ; t)
167
168
169local
170 val signalHandler: Prim.thread option ref = ref NONE
171 datatype state = Normal | InHandler
172 val state: state ref = ref Normal
173in
174 fun amInSignalHandler () = InHandler = !state
175
176 fun setSignalHandler (f: Runnable.t -> Runnable.t): unit =
177 let
178 val _ = Primitive.MLton.installSignalHandler ()
179 fun loop (): unit =
180 let
181 (* Atomic 1 *)
182 val _ = state := InHandler
183 val t = f (fromPrimitive (Prim.saved gcState))
184 val _ = state := Normal
185 val _ = Prim.finishSignalHandler gcState
186 val _ =
187 atomicSwitch
188 (fn (T r) =>
189 let
190 val _ =
191 case !r of
192 Paused (f, _) => f (fn () => ())
193 | _ => raise die "Thread.setSignalHandler saw strange thread"
194 in
195 t
196 end) (* implicit atomicEnd () *)
197 in
198 loop ()
199 end
200 val p =
201 toPrimitive
202 (new (fn () => loop () handle e => MLtonExn.topLevelHandler e))
203 val _ = signalHandler := SOME p
204 in
205 Prim.setSignalHandler (gcState, p)
206 end
207
208 fun switchToSignalHandler () =
209 let
210 (* Atomic 0 *)
211 val () = atomicBegin ()
212 (* Atomic 1 *)
213 val () = Prim.startSignalHandler gcState (* implicit atomicBegin () *)
214 (* Atomic 2 *)
215 in
216 case !signalHandler of
217 NONE => raise Fail "no signal handler installed"
218 | SOME t => Prim.switchTo t (* implicit atomicEnd() *)
219 end
220end
221
222
223local
224
225in
226 val register: int * (MLtonPointer.t -> unit) -> unit =
227 let
228 val exports =
229 Array.array (Int32.toInt (Primitive.MLton.FFI.numExports),
230 fn _ => raise Fail "undefined export")
231 val worker : (Prim.thread * Prim.thread option ref) option ref = ref NONE
232 fun mkWorker (): Prim.thread * Prim.thread option ref =
233 let
234 val thisWorker : (Prim.thread * Prim.thread option ref) option ref = ref NONE
235 val savedRef : Prim.thread option ref = ref NONE
236 fun workerLoop () =
237 let
238 (* Atomic 1 *)
239 val p = Primitive.MLton.FFI.getOpArgsResPtr ()
240 val _ = atomicEnd ()
241 (* Atomic 0 *)
242 val i = MLtonPointer.getInt32 (MLtonPointer.getPointer (p, 0), 0)
243 val _ =
244 (Array.sub (exports, Int32.toInt i) p)
245 handle e =>
246 (TextIO.output
247 (TextIO.stdErr, "Call from C to SML raised exception.\n")
248 ; MLtonExn.topLevelHandler e)
249 (* Atomic 0 *)
250 val _ = atomicBegin ()
251 (* Atomic 1 *)
252 val _ = worker := !thisWorker
253 val _ = Prim.setSaved (gcState, valOf (!savedRef))
254 val _ = savedRef := NONE
255 val _ = Prim.returnToC () (* implicit atomicEnd() *)
256 in
257 workerLoop ()
258 end
259 val workerThread = toPrimitive (new workerLoop)
260 val _ = thisWorker := SOME (workerThread, savedRef)
261 in
262 (workerThread, savedRef)
263 end
264 fun handlerLoop (): unit =
265 let
266 (* Atomic 2 *)
267 val saved = Prim.saved gcState
268 val (workerThread, savedRef) =
269 case !worker of
270 NONE => mkWorker ()
271 | SOME (workerThread, savedRef) =>
272 (worker := NONE
273 ; (workerThread, savedRef))
274 val _ = savedRef := SOME saved
275 val _ = Prim.switchTo (workerThread) (* implicit atomicEnd() *)
276 in
277 handlerLoop ()
278 end
279 val handlerThread = toPrimitive (new handlerLoop)
280 val _ = Prim.setCallFromCHandler (gcState, handlerThread)
281 in
282 fn (i, f) => Array.update (exports, i, f)
283 end
284end
285
286end