BoltLock.st
changeset 2798 ebd1bab6255e
child 2799 25cf016d4eed
equal deleted inserted replaced
2797:6ae35a95753f 2798:ebd1bab6255e
       
     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!