Import Upstream version 20180207
[hcoop/debian/mlton.git] / lib / cml / core-cml / run-cml.sml
CommitLineData
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
12structure 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