File: mutex.sml

package info (click to toggle)
mlton 20130715-3
  • links: PTS
  • area: main
  • in suites: stretch
  • size: 60,900 kB
  • ctags: 69,386
  • sloc: xml: 34,418; ansic: 17,399; lisp: 2,879; makefile: 1,605; sh: 1,254; pascal: 256; python: 143; asm: 97
file content (168 lines) | stat: -rw-r--r-- 4,943 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
open Posix.Signal MLton.Signal
      
fun for (start, stop, f) =
   let
      fun loop i =
         if i >= stop
            then ()
         else (f i; loop (i + 1))
   in
      loop start
   end
   
structure Queue:
   sig
      type 'a t

      val new: unit -> 'a t
      val enque: 'a t * 'a -> unit
      val deque: 'a t -> 'a option
   end =
   struct
      datatype 'a t = T of {front: 'a list ref, back: 'a list ref}

      fun new () = T {front = ref [], back = ref []}

      fun enque (T {back, ...}, x) = back := x :: !back

      fun deque (T {front, back}) =
         case !front of
            [] => (case !back of
                      [] => NONE
                    | l => let val l = rev l
                           in case l of
                              [] => raise Fail "deque"
                            | x :: l => (back := []; front := l; SOME x)
                           end)
          | x :: l => (front := l; SOME x) 
   end

structure Thread:
   sig
      val exit: unit -> 'a
      val run: unit -> unit
      val spawn: (unit -> unit) -> unit
      val yield: unit -> unit
      structure Mutex:
         sig
            type t

            val new: unit -> t
            val lock: t -> unit
            val unlock: t -> unit
         end
   end =
   struct
      open MLton
      open Itimer Signal Thread

      val topLevel: Thread.Runnable.t option ref = ref NONE

      local
         val threads: Thread.Runnable.t Queue.t = Queue.new ()
      in
         fun ready t = Queue.enque (threads, t)
         fun next () : Thread.Runnable.t =
            case Queue.deque threads of
               NONE => valOf (!topLevel)
             | SOME t => t
      end
      
      fun 'a exit (): 'a = switch (fn _ =>
                                   (print "exiting\n"
                                    ; next ()))
   
      fun new (f: unit -> unit): Thread.Runnable.t =
         Thread.prepare
         (Thread.new (fn () => ((f () handle _ => exit ())
                                ; exit ())),
          ())
            
      fun schedule t = (ready t; next ())

      fun yield (): unit = switch (fn t => schedule (Thread.prepare (t, ())))

      val spawn = ready o new

      fun setItimer t =
         Itimer.set (Itimer.Real,
                     {value = t,
                      interval = t})

      fun run (): unit =
         (switch (fn t =>
                  (topLevel := SOME (Thread.prepare (t, ()))
                   ; new (fn () =>
                          (setHandler (alrm, Handler.handler schedule)
                           ; setItimer (Time.fromMilliseconds 10)))))
          ; setItimer Time.zeroTime
          ; setHandler (alrm, Handler.ignore)
          ; topLevel := NONE)
            
      structure Mutex =
         struct
            datatype t = T of {locked: bool ref,
                               waiting: unit Thread.t Queue.t}
                  
            fun new () =
               T {locked = ref false,
                  waiting = Queue.new ()}

            fun lock (T {locked, waiting, ...}) =
               let
                  fun loop () =
                     (Thread.atomicBegin ()
                      ; if !locked
                           then (Thread.atomicEnd ()
                                 ; switch (fn t =>
                                           (Queue.enque (waiting, t)
                                            ; next ()))
                                 ; loop ())
                        else (locked := true
                              ; Thread.atomicEnd ()))
               in loop ()
               end
               
            fun safeUnlock (T {locked, waiting, ...}) =
               (locked := false
                ; (case Queue.deque waiting of
                      NONE => ()
                    | SOME t => ready (Thread.prepare (t,()))))

            fun unlock (m: t) =
               (Thread.atomicBegin ()
                ; safeUnlock m
                ; Thread.atomicEnd ())
         end
   end

open Thread
      
fun main (name, args) =
   let
      val m = Mutex.new ()
      val gotIt = ref false
      val _ = 
         for (0, 10, fn _ =>
              Thread.spawn
              (fn () =>
               let
                  val _ = print "starting\n"
                  fun loop i =
                     if i = 0
                        then ()
                     else (Mutex.lock m
                           ; if !gotIt
                                then raise Fail "bug"
                             else (gotIt := true
                                   ; for (0, 100000, fn _ => ())
                                   ; gotIt := false
                                   ; Mutex.unlock m
                                   ; loop (i - 1)))
               in loop 10000
               end))
   in
      run ()
   end

val _ = main ( CommandLine.name (), CommandLine.arguments () )