Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | (* run-cml.sml |
2 | * 2004 Matthew Fluet (mfluet@acm.org) | |
3 | * Ported to MLton threads. | |
4 | *) | |
5 | ||
6 | (* run-cml-fn.sml | |
7 | * | |
8 | * COPYRIGHT (c) 1996 AT&T Research. | |
9 | * COPYRIGHT (c) 1989-1991 John H. Reppy | |
10 | *) | |
11 | ||
12 | structure RunCML : RUN_CML = | |
13 | struct | |
14 | structure Assert = LocalAssert(val assert = false) | |
15 | structure Debug = LocalDebug(val debug = false) | |
16 | ||
17 | structure R = Running | |
18 | structure S = Scheduler | |
19 | structure SH = SchedulerHooks | |
20 | structure TID = ThreadID | |
21 | structure TO = TimeOut | |
22 | fun debug msg = Debug.sayDebug ([S.atomicMsg, S.tidMsg], msg) | |
23 | fun debug' msg = debug (fn () => msg) | |
24 | ||
25 | ||
26 | local | |
27 | structure Signal = MLton.Signal | |
28 | structure Itimer = MLton.Itimer | |
29 | ||
30 | fun getAlrmHandler () = | |
31 | Signal.getHandler Posix.Signal.alrm | |
32 | fun setAlrmHandler h = | |
33 | Signal.setHandler (Posix.Signal.alrm, h) | |
34 | ||
35 | fun setItimer t = | |
36 | Itimer.set (Itimer.Real, {value = t, interval = t}) | |
37 | in | |
38 | fun prepareAlrmHandler tq = | |
39 | let | |
40 | val origAlrmHandler = getAlrmHandler () | |
41 | val tq = | |
42 | case tq of | |
43 | SOME tq => tq | |
44 | | NONE => Time.fromMilliseconds 20 | |
45 | in | |
46 | (fn alrmHandler => | |
47 | (setAlrmHandler (Signal.Handler.handler (S.unwrap alrmHandler)) | |
48 | ; setItimer tq), | |
49 | fn () => | |
50 | (setItimer Time.zeroTime | |
51 | ; setAlrmHandler origAlrmHandler)) | |
52 | end | |
53 | end | |
54 | ||
55 | fun isRunning () = !R.isRunning | |
56 | ||
57 | fun reset running = | |
58 | (S.reset running | |
59 | ; SH.reset () | |
60 | ; TID.reset () | |
61 | ; TO.reset ()) | |
62 | ||
63 | fun alrmHandler thrd = | |
64 | let | |
65 | val () = Assert.assertAtomic' ("RunCML.alrmHandler", NONE) | |
66 | val () = debug' "alrmHandler" (* Atomic 1 *) | |
67 | val () = Assert.assertAtomic' ("RunCML.alrmHandler", SOME 1) | |
68 | val () = S.preempt thrd | |
69 | val () = ignore (TO.preempt ()) | |
70 | in | |
71 | S.next () | |
72 | end | |
73 | ||
74 | (* Note that SH.pauseHook is only invoked by S.next | |
75 | * when there are no threads on the ready queue; | |
76 | * Furthermore, note that alrmHandler always | |
77 | * enqueues the preepted thread (via S.preempt). | |
78 | * Hence, the ready queue is never empty | |
79 | * at the S.next in alrmHandler. Therefore, | |
80 | * pauseHook is never run within alrmHandler. | |
81 | *) | |
82 | fun pauseHook () = | |
83 | let | |
84 | val () = Assert.assertAtomic' ("RunCML.pauseHook", NONE) | |
85 | val () = debug' "pauseHook" (* Atomic 1 *) | |
86 | val () = Assert.assertAtomic' ("RunCML.pauseHook", SOME 1) | |
87 | val to = TO.preempt () | |
88 | in | |
89 | case to of | |
90 | NONE => | |
91 | (* no waiting threads *) | |
92 | S.prepFn (!SH.shutdownHook, fn () => (true, OS.Process.failure)) | |
93 | | SOME NONE => | |
94 | (* enqueued a waiting thread *) | |
95 | S.next () | |
96 | | SOME (SOME t) => | |
97 | (* a waiting thread will be ready in t time *) | |
98 | (if Time.toSeconds t <= 0 | |
99 | then () | |
100 | else S.doMasked (fn () => OS.Process.sleep t) | |
101 | ; pauseHook ()) | |
102 | end | |
103 | ||
104 | fun doit (initialProc: unit -> unit, | |
105 | tq: Time.time option) = | |
106 | let | |
107 | val () = | |
108 | if isRunning () | |
109 | then raise Fail "CML is running" | |
110 | else () | |
111 | val (installAlrmHandler, restoreAlrmHandler) = prepareAlrmHandler tq | |
112 | val ((*cleanUp*)_, status) = | |
113 | S.switchToNext | |
114 | (fn thrd => | |
115 | let | |
116 | val () = R.isRunning := true | |
117 | val () = reset true | |
118 | val () = SH.shutdownHook := S.prepend (thrd, fn arg => (S.atomicBegin (); arg)) | |
119 | val () = SH.pauseHook := pauseHook | |
120 | val () = installAlrmHandler alrmHandler | |
121 | val () = ignore (Thread.spawn initialProc) | |
122 | in | |
123 | () | |
124 | end) | |
125 | val () = restoreAlrmHandler () | |
126 | val () = reset false | |
127 | val () = R.isRunning := false | |
128 | val () = S.atomicEnd () | |
129 | in | |
130 | status | |
131 | end | |
132 | ||
133 | fun shutdown status = | |
134 | if isRunning () | |
135 | then S.switch (fn _ => S.prepVal (!SH.shutdownHook, (true, status))) | |
136 | else raise Fail "CML is not running" | |
137 | end |