|
1 " |
|
2 COPYRIGHT (c) 2012 by Claus Gittinger |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 "{ Package: 'stx:libbasic2' }" |
|
13 |
|
14 Object subclass:#BoltLock |
|
15 instanceVariableNames:'state readers numReaders waitingProcesses name' |
|
16 classVariableNames:'StateFree StateLocked StateBusy' |
|
17 poolDictionaries:'' |
|
18 category:'Kernel-Processes' |
|
19 ! |
|
20 |
|
21 !BoltLock class methodsFor:'documentation'! |
|
22 |
|
23 copyright |
|
24 " |
|
25 COPYRIGHT (c) 2012 by Claus Gittinger |
|
26 All Rights Reserved |
|
27 |
|
28 This software is furnished under a license and may be used |
|
29 only in accordance with the terms of that license and with the |
|
30 inclusion of the above copyright notice. This software may not |
|
31 be provided or otherwise made available to, or used by, any |
|
32 other person. No title to or ownership of the software is |
|
33 hereby transferred. |
|
34 " |
|
35 ! |
|
36 |
|
37 documentation |
|
38 " |
|
39 A BoltLock (aka BoldVariable) is a 3-state semaphore, |
|
40 useful to protect a resource against single writers, while allowing multiple |
|
41 readers to access the resource simultaneously. |
|
42 |
|
43 The lock is in one of 3 states: |
|
44 A; free - can be changed by a writer into the locked state, or by a reader |
|
45 into the busy state. |
|
46 |
|
47 B; busy - can only be entered by another reader. left into free by last reader. |
|
48 |
|
49 C; locked - a single writer has the lock; no other reader or writer is allowed |
|
50 to aquire it |
|
51 |
|
52 [caveat:] |
|
53 with many readers, the writers tend to make only very slow progress |
|
54 (need priority based wakeup). |
|
55 |
|
56 [instance variables:] |
|
57 state <Symbol> |
|
58 |
|
59 numReaders <Integer> number of readers holding onto the lock |
|
60 |
|
61 waitingProcesses <OrderedCollection> waiting processes - will be served first |
|
62 come first served when signalled. |
|
63 |
|
64 name <String> a debugging aid: an optional userFriendly |
|
65 name; helps to identify a semaphore easier. |
|
66 |
|
67 [see also:] |
|
68 Semaphore SemaphoreSet RecursionLock Monitor |
|
69 SharedQueue Delay |
|
70 Process ProcessorScheduler |
|
71 |
|
72 [author:] |
|
73 Claus Gittinger |
|
74 " |
|
75 ! |
|
76 |
|
77 examples |
|
78 " |
|
79 many processes synchronizing on a boltLock: |
|
80 [exBegin] |
|
81 |lock readers readWriters| |
|
82 |
|
83 lock := BoltLock new. |
|
84 |
|
85 readWriters := (1 to:10) collect:[:tNo | |
|
86 [ |
|
87 10 timesRepeat:[ |
|
88 (Random nextIntegerBetween:1 and:6) == 1 ifTrue:[ |
|
89 Transcript showCR:('thread %1: want to write...' bindWith:tNo). |
|
90 lock waitForWrite. |
|
91 Transcript showCR:('thread %1: **** write' bindWith:tNo). |
|
92 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
93 Transcript showCR:('thread %1: done writing.' bindWith:tNo). |
|
94 lock release. |
|
95 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
96 ] ifFalse:[ |
|
97 Transcript showCR:('thread %1: want to read...' bindWith:tNo). |
|
98 lock waitForRead. |
|
99 Transcript showCR:('thread %1: ---- read' bindWith:tNo). |
|
100 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
101 Transcript showCR:('thread %1: done reading.' bindWith:tNo). |
|
102 lock release. |
|
103 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
104 ]. |
|
105 ]. |
|
106 Transcript showCR:('thread %1: finished.'). |
|
107 ] newProcess name:('rw%1' bindWith:tNo). |
|
108 ]. |
|
109 |
|
110 readers := (11 to:20) collect:[:tNo | |
|
111 [ |
|
112 10 timesRepeat:[ |
|
113 Transcript showCR:('thread %1: want to read...' bindWith:tNo). |
|
114 lock waitForRead. |
|
115 Transcript showCR:('thread %1: ---- read' bindWith:tNo). |
|
116 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
117 Transcript showCR:('thread %1: done.' bindWith:tNo). |
|
118 lock release. |
|
119 Delay waitForSeconds:(Random nextIntegerBetween:1 and:4). |
|
120 ]. |
|
121 Transcript showCR:('thread %1: finished.'). |
|
122 ] newProcess name:('r%1' bindWith:tNo). |
|
123 ]. |
|
124 |
|
125 readWriters do:[:t | t resume]. |
|
126 readers do:[:t | t resume]. |
|
127 [exEnd] |
|
128 " |
|
129 ! ! |
|
130 |
|
131 !BoltLock class methodsFor:'instance creation'! |
|
132 |
|
133 new |
|
134 "return an initialized instance" |
|
135 |
|
136 ^ self basicNew initialize. |
|
137 ! ! |
|
138 |
|
139 !BoltLock class methodsFor:'class initialization'! |
|
140 |
|
141 initialize |
|
142 StateFree := #free. |
|
143 StateBusy := #busy. |
|
144 StateLocked := #locked. |
|
145 |
|
146 "Created: / 06-08-2012 / 15:49:44 / cg" |
|
147 ! ! |
|
148 |
|
149 !BoltLock methodsFor:'initialization'! |
|
150 |
|
151 initialize |
|
152 "Invoked when a new instance is created." |
|
153 |
|
154 state := StateFree. |
|
155 numReaders := 0. |
|
156 waitingProcesses := OrderedCollection new. |
|
157 |
|
158 "Modified: / 06-08-2012 / 16:03:06 / cg" |
|
159 ! ! |
|
160 |
|
161 !BoltLock methodsFor:'private'! |
|
162 |
|
163 addWaitingProcess:aProcess |
|
164 "add aProcess to the list of waiting processes. |
|
165 all processes are ordered first-come-first-serve. |
|
166 |
|
167 NOTE: must be called with blocked interrupts" |
|
168 |
|
169 "for now" |
|
170 waitingProcesses isNil ifTrue:[ |
|
171 waitingProcesses := Array with:aProcess |
|
172 ] ifFalse:[ |
|
173 waitingProcesses isArray ifTrue:[ |
|
174 waitingProcesses := OrderedCollection withAll:waitingProcesses. |
|
175 ]. |
|
176 waitingProcesses add:aProcess. |
|
177 ]. |
|
178 |
|
179 "Created: / 06-08-2012 / 16:05:38 / cg" |
|
180 ! |
|
181 |
|
182 removeWaitingProcess:aProcess |
|
183 "remove aProcess from the list of waiting processes |
|
184 NO action if it is not in the list. |
|
185 |
|
186 NOTE: must be called with blocked interrupts" |
|
187 |
|
188 |nWaiting| |
|
189 |
|
190 nWaiting := waitingProcesses size. |
|
191 nWaiting == 0 ifTrue:[^ self]. |
|
192 |
|
193 nWaiting == 1 ifTrue:[ |
|
194 (waitingProcesses at:1) == aProcess ifTrue:[ |
|
195 waitingProcesses := nil. |
|
196 ]. |
|
197 ^ self. |
|
198 ]. |
|
199 waitingProcesses removeIdentical:aProcess ifAbsent:[]. |
|
200 |
|
201 "Created: / 06-08-2012 / 16:06:00 / cg" |
|
202 ! |
|
203 |
|
204 wakeupWaiters |
|
205 "remove all waiting processes from the list of waiting processes |
|
206 and resume them. |
|
207 NOTE: Must be called when known that waitingProcesses is nonNil and |
|
208 also with blocked interrupts" |
|
209 |
|
210 |processes| |
|
211 |
|
212 processes := waitingProcesses. |
|
213 "/ do not set to nil - a waiting process may be suspended and will not be resumed... |
|
214 "/ waitingProcesses := nil. |
|
215 |
|
216 "/ todo: resume by priority; higher first. |
|
217 processes do:[:eachProcess | |
|
218 Transcript showCR:(' wakeup thread %1' bindWith:eachProcess name). |
|
219 Processor resume:eachProcess |
|
220 ]. |
|
221 |
|
222 "Created: / 06-08-2012 / 16:05:01 / cg" |
|
223 ! ! |
|
224 |
|
225 !BoltLock methodsFor:'waiting'! |
|
226 |
|
227 release |
|
228 "release the lock" |
|
229 |
|
230 |activeProcess wasBlocked| |
|
231 |
|
232 wasBlocked := OperatingSystem blockInterrupts. |
|
233 "/ Transcript showCR:' release in state ',state. |
|
234 activeProcess := Processor activeProcess. |
|
235 state == StateLocked ifTrue:[ |
|
236 "I am the writer" |
|
237 state := StateFree. |
|
238 ] ifFalse:[ |
|
239 "I am a reader" |
|
240 numReaders := numReaders - 1. |
|
241 numReaders == 0 ifTrue:[ |
|
242 state := StateFree. |
|
243 ] |
|
244 ]. |
|
245 state == StateFree ifTrue:[ |
|
246 waitingProcesses notEmptyOrNil ifTrue:[ |
|
247 self wakeupWaiters. |
|
248 ]. |
|
249 ]. |
|
250 wasBlocked ifFalse:[OperatingSystem unblockInterrupts]. |
|
251 |
|
252 "Created: / 06-08-2012 / 16:00:04 / cg" |
|
253 ! |
|
254 |
|
255 waitForRead |
|
256 "wait for the lock in order to read" |
|
257 |
|
258 |activeProcess wasBlocked| |
|
259 |
|
260 wasBlocked := OperatingSystem blockInterrupts. |
|
261 |
|
262 "/ Transcript showCR:' waitForRead in state ',state. |
|
263 state == StateLocked ifTrue:[ |
|
264 "being written; wait until released" |
|
265 activeProcess := Processor activeProcess. |
|
266 [ |
|
267 self addWaitingProcess:activeProcess. |
|
268 [ |
|
269 activeProcess suspendWithState:#waitForRead |
|
270 ] ifCurtailed:[ |
|
271 "interrupts are not blocked when entered through Processor>>#interruptActive" |
|
272 OperatingSystem blockInterrupts. |
|
273 self removeWaitingProcess:activeProcess. |
|
274 wasBlocked ifFalse:[OperatingSystem unblockInterrupts]. |
|
275 ]. |
|
276 self removeWaitingProcess:activeProcess. |
|
277 ] doUntil:[ state ~~ StateLocked]. |
|
278 ]. |
|
279 |
|
280 numReaders := numReaders + 1. |
|
281 state := StateBusy. |
|
282 |
|
283 wasBlocked ifFalse:[OperatingSystem unblockInterrupts]. |
|
284 |
|
285 "Created: / 06-08-2012 / 15:49:08 / cg" |
|
286 ! |
|
287 |
|
288 waitForWrite |
|
289 "wait for the lock in order to read" |
|
290 |
|
291 |activeProcess wasBlocked| |
|
292 |
|
293 wasBlocked := OperatingSystem blockInterrupts. |
|
294 |
|
295 "/ Transcript showCR:' waitForWrite in state ',state. |
|
296 state ~~ StateFree ifTrue:[ |
|
297 "being read or written" |
|
298 activeProcess := Processor activeProcess. |
|
299 [ |
|
300 self addWaitingProcess:activeProcess. |
|
301 [ |
|
302 activeProcess suspendWithState:#waitForWrite |
|
303 ] ifCurtailed:[ |
|
304 "interrupts are not blocked when entered through Processor>>#interruptActive" |
|
305 OperatingSystem blockInterrupts. |
|
306 self removeWaitingProcess:activeProcess. |
|
307 wasBlocked ifFalse:[OperatingSystem unblockInterrupts]. |
|
308 ]. |
|
309 self removeWaitingProcess:activeProcess. |
|
310 ] doUntil:[ state == StateFree]. |
|
311 ]. |
|
312 |
|
313 state := StateLocked. |
|
314 |
|
315 wasBlocked ifFalse:[OperatingSystem unblockInterrupts]. |
|
316 |
|
317 "Created: / 06-08-2012 / 15:54:05 / cg" |
|
318 ! ! |
|
319 |
|
320 !BoltLock class methodsFor:'documentation'! |
|
321 |
|
322 version |
|
323 ^ '$Header: /cvs/stx/stx/libbasic2/BoltLock.st,v 1.1 2012-08-06 14:32:01 cg Exp $' |
|
324 ! |
|
325 |
|
326 version_CVS |
|
327 ^ '$Header: /cvs/stx/stx/libbasic2/BoltLock.st,v 1.1 2012-08-06 14:32:01 cg Exp $' |
|
328 ! ! |
|
329 |
|
330 BoltLock initialize! |