Commit | Line | Data |
---|---|---|
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 | ||
9 | structure MLtonThread:> MLTON_THREAD_EXTRA = | |
10 | struct | |
11 | ||
12 | structure Prim = Primitive.MLton.Thread | |
13 | ||
14 | fun 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 | ||
21 | val gcState = Primitive.MLton.GCState.gcState | |
22 | ||
23 | structure AtomicState = | |
24 | struct | |
25 | datatype t = NonAtomic | Atomic of int | |
26 | end | |
27 | ||
28 | local | |
29 | open Prim | |
30 | in | |
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) | |
37 | end | |
38 | ||
39 | fun atomically f = | |
40 | (atomicBegin (); DynamicWind.wind (f, atomicEnd)) | |
41 | ||
42 | datatype '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 | ||
49 | datatype 'a t = T of 'a thread ref | |
50 | ||
51 | structure Runnable = | |
52 | struct | |
53 | type t = unit t | |
54 | end | |
55 | ||
56 | fun 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 | ||
68 | fun prepare (t: 'a t, v: 'a): Runnable.t = | |
69 | prepend (t, fn () => v) | |
70 | ||
71 | fun new f = T (ref (New f)) | |
72 | ||
73 | local | |
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 | |
104 | in | |
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) | |
143 | end | |
144 | ||
145 | fun fromPrimitive (t: Prim.thread): Runnable.t = | |
146 | T (ref (Interrupted t)) | |
147 | ||
148 | fun 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 | ||
169 | local | |
170 | val signalHandler: Prim.thread option ref = ref NONE | |
171 | datatype state = Normal | InHandler | |
172 | val state: state ref = ref Normal | |
173 | in | |
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 | |
220 | end | |
221 | ||
222 | ||
223 | local | |
224 | ||
225 | in | |
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 | |
284 | end | |
285 | ||
286 | end |