initial checkin
authorClaus Gittinger <cg@exept.de>
Mon, 06 Aug 2012 16:32:01 +0200
changeset 2798 ebd1bab6255e
parent 2797 6ae35a95753f
child 2799 25cf016d4eed
initial checkin
BoltLock.st
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BoltLock.st	Mon Aug 06 16:32:01 2012 +0200
@@ -0,0 +1,330 @@
+"
+ COPYRIGHT (c) 2012 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+"{ Package: 'stx:libbasic2' }"
+
+Object subclass:#BoltLock
+	instanceVariableNames:'state readers numReaders waitingProcesses name'
+	classVariableNames:'StateFree StateLocked StateBusy'
+	poolDictionaries:''
+	category:'Kernel-Processes'
+!
+
+!BoltLock class methodsFor:'documentation'!
+
+copyright
+"
+ COPYRIGHT (c) 2012 by Claus Gittinger
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+!
+
+documentation
+"
+    A BoltLock (aka BoldVariable) is a 3-state semaphore, 
+    useful to protect a resource against single writers, while allowing multiple
+    readers to access the resource simultaneously.
+
+    The lock is in one of 3 states:
+        A; free - can be changed by a writer into the locked state, or by a reader
+                  into the busy state.
+
+        B; busy - can only be entered by another reader. left into free by last reader.
+
+        C; locked  - a single writer has the lock; no other reader or writer is allowed
+                     to aquire it
+
+    [caveat:]
+        with many readers, the writers tend to make only very slow progress
+        (need priority based wakeup).
+
+    [instance variables:]
+        state                   <Symbol>
+
+        numReaders              <Integer>               number of readers holding onto the lock
+
+        waitingProcesses        <OrderedCollection>     waiting processes - will be served first
+                                                        come first served when signalled.
+
+        name                    <String>                a debugging aid: an optional userFriendly
+                                                        name; helps to identify a semaphore easier.
+
+    [see also:]
+        Semaphore SemaphoreSet RecursionLock Monitor
+        SharedQueue Delay 
+        Process ProcessorScheduler
+
+    [author:]
+        Claus Gittinger
+"
+!
+
+examples
+"
+    many processes synchronizing on a boltLock:
+                                                        [exBegin]
+        |lock readers readWriters|
+
+        lock := BoltLock new.
+
+        readWriters := (1 to:10) collect:[:tNo |
+                        [
+                            10 timesRepeat:[
+                                (Random nextIntegerBetween:1 and:6) == 1 ifTrue:[
+                                    Transcript showCR:('thread %1: want to write...' bindWith:tNo).
+                                    lock waitForWrite.
+                                    Transcript showCR:('thread %1: **** write' bindWith:tNo).
+                                    Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                                    Transcript showCR:('thread %1: done writing.' bindWith:tNo).
+                                    lock release.
+                                    Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                                ] ifFalse:[
+                                    Transcript showCR:('thread %1: want to read...' bindWith:tNo).
+                                    lock waitForRead.
+                                    Transcript showCR:('thread %1: ---- read' bindWith:tNo).
+                                    Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                                    Transcript showCR:('thread %1: done reading.' bindWith:tNo).
+                                    lock release.
+                                    Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                                ].
+                            ].
+                            Transcript showCR:('thread %1: finished.').
+                       ] newProcess name:('rw%1' bindWith:tNo).
+                    ].
+
+        readers := (11 to:20) collect:[:tNo |
+                        [
+                            10 timesRepeat:[
+                                Transcript showCR:('thread %1: want to read...' bindWith:tNo).
+                                lock waitForRead.
+                                Transcript showCR:('thread %1: ---- read' bindWith:tNo).
+                                Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                                Transcript showCR:('thread %1: done.' bindWith:tNo).
+                                lock release.
+                                Delay waitForSeconds:(Random nextIntegerBetween:1 and:4).
+                            ].
+                            Transcript showCR:('thread %1: finished.').
+                       ] newProcess name:('r%1' bindWith:tNo).
+                    ].
+
+        readWriters do:[:t | t resume].
+        readers do:[:t | t resume].
+                                                        [exEnd]
+"
+! !
+
+!BoltLock class methodsFor:'instance creation'!
+
+new
+    "return an initialized instance"
+
+    ^ self basicNew initialize.
+! !
+
+!BoltLock class methodsFor:'class initialization'!
+
+initialize
+    StateFree := #free.
+    StateBusy := #busy.
+    StateLocked := #locked.
+
+    "Created: / 06-08-2012 / 15:49:44 / cg"
+! !
+
+!BoltLock methodsFor:'initialization'!
+
+initialize
+    "Invoked when a new instance is created."
+
+    state := StateFree.
+    numReaders := 0.
+    waitingProcesses := OrderedCollection new.
+
+    "Modified: / 06-08-2012 / 16:03:06 / cg"
+! !
+
+!BoltLock methodsFor:'private'!
+
+addWaitingProcess:aProcess
+    "add aProcess to the list of waiting processes.
+     all processes are ordered first-come-first-serve.
+
+     NOTE: must be called with blocked interrupts"
+
+    "for now"
+    waitingProcesses isNil ifTrue:[
+        waitingProcesses := Array with:aProcess
+    ] ifFalse:[
+        waitingProcesses isArray ifTrue:[
+            waitingProcesses := OrderedCollection withAll:waitingProcesses.
+        ].
+        waitingProcesses add:aProcess.
+    ].
+
+    "Created: / 06-08-2012 / 16:05:38 / cg"
+!
+
+removeWaitingProcess:aProcess
+    "remove aProcess from the list of waiting processes
+     NO action if it is not in the list.
+
+     NOTE: must be called with blocked interrupts"
+
+    |nWaiting|
+
+    nWaiting := waitingProcesses size.
+    nWaiting == 0 ifTrue:[^ self].
+
+    nWaiting == 1 ifTrue:[
+        (waitingProcesses at:1) == aProcess ifTrue:[
+            waitingProcesses := nil.
+        ].
+        ^ self.
+    ].
+    waitingProcesses removeIdentical:aProcess ifAbsent:[].
+
+    "Created: / 06-08-2012 / 16:06:00 / cg"
+!
+
+wakeupWaiters
+    "remove all waiting processes from the list of waiting processes
+     and resume them. 
+     NOTE: Must be called when known that waitingProcesses is nonNil and
+           also with blocked interrupts"
+
+    |processes|
+
+    processes := waitingProcesses.
+    "/ do not set to nil - a waiting process may be suspended and will not be resumed...
+    "/    waitingProcesses := nil.
+
+    "/ todo: resume by priority; higher first.
+    processes do:[:eachProcess | 
+        Transcript showCR:('   wakeup thread %1' bindWith:eachProcess name).
+        Processor resume:eachProcess
+    ].
+
+    "Created: / 06-08-2012 / 16:05:01 / cg"
+! !
+
+!BoltLock methodsFor:'waiting'!
+
+release
+    "release the lock"
+
+    |activeProcess wasBlocked|
+
+    wasBlocked := OperatingSystem blockInterrupts.
+    "/ Transcript showCR:'  release in state ',state.
+    activeProcess := Processor activeProcess.
+    state == StateLocked ifTrue:[
+        "I am the writer"
+        state := StateFree.
+    ] ifFalse:[
+        "I am a reader"
+        numReaders := numReaders - 1.
+        numReaders == 0 ifTrue:[
+            state := StateFree.
+        ]
+    ].
+    state == StateFree ifTrue:[
+        waitingProcesses notEmptyOrNil ifTrue:[
+            self wakeupWaiters.
+        ].
+    ].
+    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+
+    "Created: / 06-08-2012 / 16:00:04 / cg"
+!
+
+waitForRead
+    "wait for the lock in order to read"
+
+    |activeProcess wasBlocked|
+
+    wasBlocked := OperatingSystem blockInterrupts.
+
+    "/ Transcript showCR:'  waitForRead in state ',state.
+    state == StateLocked ifTrue:[
+        "being written; wait until released"
+        activeProcess := Processor activeProcess.
+        [
+            self addWaitingProcess:activeProcess.
+            [
+                activeProcess suspendWithState:#waitForRead
+            ] ifCurtailed:[
+                "interrupts are not blocked when entered through Processor>>#interruptActive"
+                OperatingSystem blockInterrupts.
+                self removeWaitingProcess:activeProcess.
+                wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+            ].
+            self removeWaitingProcess:activeProcess.
+        ] doUntil:[ state ~~ StateLocked].
+    ].
+
+    numReaders := numReaders + 1.
+    state := StateBusy.
+
+    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+
+    "Created: / 06-08-2012 / 15:49:08 / cg"
+!
+
+waitForWrite
+    "wait for the lock in order to read"
+
+    |activeProcess wasBlocked|
+
+    wasBlocked := OperatingSystem blockInterrupts.
+
+    "/ Transcript showCR:'  waitForWrite in state ',state.
+    state ~~ StateFree ifTrue:[
+        "being read or written"
+        activeProcess := Processor activeProcess.
+        [
+            self addWaitingProcess:activeProcess.
+            [
+                activeProcess suspendWithState:#waitForWrite
+            ] ifCurtailed:[
+                "interrupts are not blocked when entered through Processor>>#interruptActive"
+                OperatingSystem blockInterrupts.
+                self removeWaitingProcess:activeProcess.
+                wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+            ].
+            self removeWaitingProcess:activeProcess.
+        ] doUntil:[ state == StateFree].
+    ].
+
+    state := StateLocked.
+
+    wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+
+    "Created: / 06-08-2012 / 15:54:05 / cg"
+! !
+
+!BoltLock class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/BoltLock.st,v 1.1 2012-08-06 14:32:01 cg Exp $'
+!
+
+version_CVS
+    ^ '$Header: /cvs/stx/stx/libbasic2/BoltLock.st,v 1.1 2012-08-06 14:32:01 cg Exp $'
+! !
+
+BoltLock initialize!