File: programs.lisp

package info (click to toggle)
acl2 7.2dfsg-3
  • links: PTS
  • area: main
  • in suites: stretch
  • size: 198,968 kB
  • ctags: 182,300
  • sloc: lisp: 2,415,261; ansic: 5,675; perl: 5,577; xml: 3,576; sh: 3,255; cpp: 2,835; makefile: 2,440; ruby: 2,402; python: 778; ml: 763; yacc: 709; csh: 355; php: 171; lex: 162; tcl: 44; java: 24; asm: 23; haskell: 17
file content (218 lines) | stat: -rw-r--r-- 6,562 bytes parent folder | download | duplicates (6)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
(in-package "ACL2")

#|

  programs.lisp
  ~~~~~~~~~~~~~

In this book, we define a "simplified" bakery implementation and an abstract
spec. We will show that the implementation "matches" the spec upto finite
stuttering. Notice that our implementation is "simpler than the exact bakery
defined by Lamport. Lamport claims that bakery does not depend on any
lower-level mutual exclusion. We however, still use a lower-level mutual
exclusion through the "cas" instruction. While this is thus a restricted
implementation, we note that it is still practical since "cas" is an
instruction often provided as atomic on most systems.

|#

(include-book "variables")
(include-book "lexicographic")
(include-book "fairenv")



;; BEGIN simplified Bakery implementation

(defun cas (shared old new)
  (if (<= shared old)
      (list T new)
    (list nil shared)))

(defun successful-cas (shared old new)
  (first (cas shared old new)))

(defun casval (shared old new)
  (second (cas shared old new)))

(defun bake+-p (p in ps max)
  (let ((curr (<- ps (current p))))
      (case (loc p)
	(0 (>p :loc 1 :choosing T))
	(1 (>p :loc 2 :temp max :status 'interested))
	(2 (>p :loc 3 :pos (1+ (temp p))))
	(3 (>p :loc 4 :status 'wait))
	(4 (>p :loc 5 :choosing nil))
	(5 (>p :loc 6 :indices (keys ps)))
	(6 (if (endp (indices p))
	       (>p :loc 9 :status 'go)
	     (>p :loc 7 :current (first (indices p)))))
	(7 (if (choosing curr) p
	     (>p :loc 8)))
	(8 (if (and (pos curr)
		    (lex< (pos curr) (current p) (pos p) in))
	       p
	     (>p :loc 6 :indices (rest (indices p)))))
	(t (>p :loc 0 :pos nil :status 'idle)))))

;; For correspondence with the spec we are dealing with, we need a
;; queue, and a bucket in this implementation. The bucket will
;; periodically be flushed into the queue.

(defun bake+-b (p bucket in max)
  (cond ((equal (loc p) 1) (insert in bucket))
	((and (equal (loc p) 3)
	      (successful-cas max (temp p) (pos p)))
	 nil)
	(t bucket)))


(defun bake+-q (p queue bucket max)
  (cond ((and (equal (loc p) 3)
	      (successful-cas max (temp p) (pos p)))
	 (append queue bucket))
	((equal (loc p) 9) (rest queue))
        (t queue)))

;; Note that the implementation now depends on the shared variable
;; max, which will be set by atomic compare-and-swap operations.

(defun bake+-max (p max)
  (if (equal (loc p) 3)
      (casval max (temp p) (pos p))
    max))

(defun bake+-go (p go id)
  (cond ((and (equal (loc p) 6)
              (endp (indices p)))
         id)
        ((equal (loc p) 9)
         nil)
        (t go)))

;; So we organize the whole step function here.

;; st should be a record of four fields:

;; procs --- a vector of process local variables, as described in the
;;           step function.
;; queue --- a list of waiting processes.
;; bucket --- a list of processes that are interested in getting to
;;            the queue, but have not been flushed to the queue as
;;            yet.
;; max --- a natural number, determining the highest position value
;;         chosen. NOTE: max grows unbounded.


;; Comments on max: We could have chosen an implementation that resets
;;                  max. However, I dont see how to do it and preserve
;;                  correctness, while still preserving boundedness of
;;                  max. So I dont go that route and unduly complicate
;;                  matters. In short, I believe (without proof) that
;;                  unboundedness of max is a negative feature of the
;;                  bakery algorithm in general, and has nothing to do
;;                  with this particular simple implementation.

(defun bake+ (st in)
  (let* ((procs (procs st))
         (max (maxval st))
         (bucket (bucket st))
         (go (get-go st))
         (queue (queue st))
         (proc (<- procs in)))
    (>st :procs (-> procs in (bake+-p proc in procs max))
	 :maxval (bake+-max proc max)
	 :bucket (bake+-b proc bucket in max)
	 :queue (bake+-q proc queue bucket max)
         :go (bake+-go proc go in))))

(defun fair-bake+ (st X)
  (let* ((procs (procs st))
         (max (maxval st))
         (bucket (bucket st))
         (queue (queue st))
         (env (env st))
         (go (get-go st))
         (in (fair-select env X))
         (proc (<- procs in)))
    (>st :env (fair-step env X)
         :procs (-> procs in (bake+-p proc in procs max))
	 :maxval (bake+-max proc max)
	 :bucket (bake+-b proc bucket in max)
	 :queue (bake+-q proc queue bucket max)
         :go (bake+-go proc go in))))




;; END Bakery Implementation

;; BEGIN centralized Bakery. This is the spec we will show
;; correspondence to. It might not be the final spec we will
;; eventually deal with, but it will suffice for now.

(defun cent-p (p q id)
  (case (status p)
        (idle (>p :status 'interested))
        (interested (>p :status 'wait))
	(wait (>p :status (if (equal (first q) id)
                              'go 'wait)))
        (t (>p :status 'idle))))

(defun cent-b (p bucket id)
  (case (status p)
    (idle (insert id bucket))
    (interested (if (memberp id bucket) nil bucket))
    (t bucket)))

(defun cent-q (p q id bucket)
  (case (status p)
        (interested (if (memberp id bucket) (append q bucket) q))
        (go (rest q))
        (t q)))

(defun cent-go (p go q id)
  (cond ((and (equal (status p) 'wait)
              (equal (first q) id))
         id)
        ((equal (status p) 'go)
         nil)
        (t go)))

(defun cent (st in)
  (let* ((procs  (procs st))
	 (bucket (bucket st))
         (queue  (queue st))
         (go (get-go st))
         (proc   (<- procs in)))
    (>st :procs (-> procs in (cent-p proc queue in))
	 :bucket (cent-b proc bucket in)
         :queue (cent-q proc queue in bucket)
         :go (cent-go proc go queue in))))

;; END centralized bakery ;;

;; Now the part about correspondence of the bake+ algorithm with cent is the
;; big problem. However, for reason of cleanliness, we will define a much
;; simpler spec, and show that the cent is a stuttering refinement of tghe
;; spec.

;; Begin spec definition.

(defun add-to-procs (procs i)
  (insert i procs))

(defun spec (st i)
  (>st :go (cond ((and (not (get-go st))
                       (not (memberp i (procs st))))
                  nil)
                 ((get-go st) nil)
                 (t i))
       :procs (add-to-procs (procs st) i)))

(defun legal (st i)
  (and i ;; cannot be nil since we have already taken the nil
         ;; to mark that no one is in critical section.
       (implies (get-go st)
                (equal i (get-go st)))))