--- a/Semaphore.st Wed Jul 10 12:20:29 2002 +0200
+++ b/Semaphore.st Wed Jul 10 13:10:24 2002 +0200
@@ -1,6 +1,6 @@
"
COPYRIGHT (c) 1993 by Claus Gittinger
- All Rights Reserved
+ 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
@@ -13,10 +13,10 @@
"{ Package: 'stx:libbasic' }"
Object subclass:#Semaphore
- instanceVariableNames:'count waitingProcesses lastOwnerID name'
- classVariableNames:''
- poolDictionaries:''
- category:'Kernel-Processes'
+ instanceVariableNames:'count waitingProcesses lastOwnerID name'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Kernel-Processes'
!
!Semaphore class methodsFor:'documentation'!
@@ -24,7 +24,7 @@
copyright
"
COPYRIGHT (c) 1993 by Claus Gittinger
- All Rights Reserved
+ 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
@@ -44,6 +44,14 @@
Semaphore>>signal.
If the resource has been already available before the wait, no suspending is
done, but the resource immediately allocated.
+ The resource internally keeps a count, the number of times the resource can be
+ allocated. If the semaphore is created with a count greater than 1, the sema
+ can be waited-upon that many times without blocking.
+ On the other hand, if initialized with a negative count, the semaphore
+ must be signalled that many times more often in order for a wait to not block.
+ In other words: whenever the semaphore has a count greater than zero,
+ the wait operation will proceed. Otherwise, it will block until the count has
+ been incremented by signal operations to be greater than zero.
There are also semaphores for mutual access to a critical region
(Semaphore>>forMutualExclusion and Semaphore>>critical:).
@@ -64,7 +72,13 @@
region to be entered twice - NOT EVEN by the same process.
That means, that a recursive attempt to enter that section leads
to a deadlock.
- Use a RecursionLock instead of a semaphore to avoid this.
+ Use a RecursionLock instead, to avoid this.
+
+ Hint:
+ now (Jul2002), Semaphores now allow for a negative count; this allows for
+ a sync-point to be implemented easily (i.e. to wait for multiple other processes
+ to arrive at a sync-point).
+ See examples.
[instance variables:]
@@ -187,6 +201,62 @@
block value:[]. 'this works'.
block value:[block value:[] ]. 'this deadlocks'.
[exEnd]
+
+
+ Wait for multiple processes to arrive at a sync-point:
+ [exBegin]
+ |syncSema proceedSema thread1 thread2 thread3|
+
+ syncSema := Semaphore new.
+ syncSema setCount:(1-3).
+ proceedSema := Semaphore new.
+
+ thread1 := [
+ Transcript showCR:'here is thread 1; now busy ...'.
+ Delay waitForSeconds:(2 + (Random nextIntegerBetween:2 and:4)).
+ Transcript showCR:'here is thread 1 again - now syncing.'.
+ syncSema signal.
+ Transcript showCR:'thread 1 is waiting for all others...'.
+ proceedSema wait.
+ Transcript showCR:'thread 1 done.'.
+ ] newProcess.
+
+ thread2 := [
+ Transcript showCR:'here is thread 2; now busy ...'.
+ Delay waitForSeconds:(3 + (Random nextIntegerBetween:2 and:4)).
+ Transcript showCR:'here is thread 2 again - now syncing.'.
+ syncSema signal.
+ Transcript showCR:'thread 2 is waiting for all others...'.
+ proceedSema wait.
+ Transcript showCR:'thread 2 done.'.
+ ] newProcess.
+
+ thread3 := [
+ Transcript showCR:'here is thread 3; now busy ...'.
+ Delay waitForSeconds:(4 + (Random nextIntegerBetween:2 and:4)).
+ Transcript showCR:'here is thread 3 again - now syncing.'.
+ syncSema signal.
+ Transcript showCR:'thread 3 is waiting for all others...'.
+ proceedSema wait.
+ Transcript showCR:'thread 3 done.'.
+ ] newProcess.
+
+ thread1 priority:7.
+ thread2 priority:6.
+ thread3 priority:9.
+
+ thread1 resume.
+ thread2 resume.
+ thread3 resume.
+
+ Transcript showCR:'main thread: now waiting for other threads...'.
+ syncSema wait.
+ Transcript showCR:'main thread: all other threads at syncPoint.'.
+ Delay waitForSeconds:2.
+ Transcript showCR:'main thread: now let them proceed...'.
+ proceedSema signalForAll.
+ Transcript showCR:'main thread: done.'.
+ [exEnd]
"
! !
@@ -223,25 +293,19 @@
"interface for SemaphoreSet.
If the semaphore is available, decrement it and return true.
Otherwise register our process to be wakened up once the semaphore is available
- and return false..
+ and return false.
+ ATTENTION: this must be invoked with OperatingSystem-interrupts-blocked.
"
- "
- bad ST/X trick (needs change, when multiProcessor support is added):
- this works only since interrupts are only serviced at
- message send and method-return time ....
- If you add a message send into the ifTrue:-block, things will
- go mad ... (especially be careful when adding a debugPrint-here)
- "
- count ~~ 0 ifTrue:[
- count := count - 1.
- count == 0 ifTrue:[
- lastOwnerID := Processor activeProcessId.
- ].
- ^ true
+ count > 0 ifTrue:[
+ count := count - 1.
+ count == 0 ifTrue:[
+ lastOwnerID := Processor activeProcessId.
+ ].
+ ^ true
].
(waitingProcesses identityIndexOf:process) == 0 ifTrue:[
- waitingProcesses add:process.
+ waitingProcesses add:process.
].
^ false
@@ -342,9 +406,11 @@
wouldBlock
"return true, if the receiver would block the activeProcess
- if a wait was performed. False otherwise."
+ if a wait was performed. False otherwise.
+ Attention: if asked without some global lock (blockedInterrupts),
+ the returned value may be wrong right away."
- ^ count == 0
+ ^ count <= 0
! !
!Semaphore methodsFor:'testing'!
@@ -370,8 +436,18 @@
"evaluate aBlock as a critical region; the receiver must be
created using Semaphore>>forMutualExclusion"
- self wait.
- ^ aBlock ensure:[self signal].
+ |retVal gotSema|
+
+ [
+ gotSema := self wait.
+ retVal := aBlock value.
+ ] valueOnUnwindDo:[
+ "/ be careful - the unwind may occur both while waiting
+ "/ AND while evaluating the block.
+ gotSema notNil ifTrue:[self signal].
+ ].
+ self signal.
+ ^ retVal
"
the example below is stupid (it should use a SharedQueue,
@@ -485,21 +561,21 @@
signalOnce
"wakeup waiters - but only once.
- I.e. if the semaphore has already been signalled, this
- is ignored."
+ I.e. if the semaphore has already been signalled, this is ignored."
+
+ |wasBlocked p|
- |wasBlocked|
-
- count == 0 ifTrue:[
+ count <= 0 ifTrue:[
wasBlocked := OperatingSystem blockInterrupts.
+ "/ check again - now interrupts are blocked.
[
- |p|
-
- count == 0 ifTrue:[
- count := 1.
- p := waitingProcesses removeFirstIfAbsent:nil.
- p notNil ifTrue:[
- Processor resume:p.
+ count <= 0 ifTrue:[
+ count := count + 1.
+ count == 1 ifTrue:[
+ p := waitingProcesses removeFirstIfAbsent:nil.
+ p notNil ifTrue:[
+ Processor resume:p.
+ ].
].
].
] ensure:[
@@ -515,49 +591,38 @@
|activeProcess wasBlocked|
- "
- bad ST/X trick (needs change, when multiProcessor support is added):
- this works only since interrupts are only serviced at
- message send and method-return time ....
- If you add a message send between the compare and the decrement,
- things will go mad ... (especially be careful when adding a debugPrint-here)
- "
- count ~~ 0 ifTrue:[
- count := count - 1.
- count == 0 ifTrue:[
- lastOwnerID := Processor activeProcessId.
+ wasBlocked := OperatingSystem blockInterrupts.
+
+ count <= 0 ifTrue:[
+ activeProcess := Processor activeProcess.
+
+ "
+ need a while-loop here, since more than one process may
+ wait for it and another one may also wake up.
+ Thus, the count is not always non-zero after returning from
+ suspend.
+ "
+ [count <= 0] whileTrue:[
+ waitingProcesses add:activeProcess.
+ "
+ for some more descriptive info in processMonitor ...
+ ... set the state to #wait (instead of #suspend)
+ "
+ [
+ activeProcess suspendWithState:#wait
+ ] valueOnUnwindDo:[
+ waitingProcesses removeIdentical:activeProcess ifAbsent:[].
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ].
+
+ count <= 0 ifTrue:[
+ "/ care for someone manually resuming me (i.e. semaphore still not avail)...
+ "/ being multiple times on waitingProcesses
+ waitingProcesses removeIdentical:activeProcess ifAbsent:[].
+ ]
].
- ^ self
].
- activeProcess := Processor activeProcess.
-
- wasBlocked := OperatingSystem blockInterrupts.
- "
- need a while-loop here, since more than one process may
- wait for it and another one may also wake up.
- Thus, the count is not always non-zero after returning from
- suspend.
- "
- [count == 0] whileTrue:[
- waitingProcesses add:activeProcess.
- "
- for some more descriptive info in processMonitor ...
- ... set the state to #wait (instead of #suspend)
- "
- [
- activeProcess suspendWithState:#wait
- ] ifCurtailed:[
- waitingProcesses removeIdentical:activeProcess ifAbsent:[].
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ].
-
- count == 0 ifTrue:[
- "/ care for someone manually resuming me ...
- "/ being multiple times on waitingProcesses
- waitingProcesses removeIdentical:activeProcess ifAbsent:[].
- ]
- ].
count := count - 1.
count == 0 ifTrue:[
lastOwnerID := Processor activeProcessId.
@@ -574,14 +639,7 @@
|activeProcess wasBlocked|
- "
- bad ST/X trick (needs change, when multiProcessor support is added):
- this works only since interrupts are only serviced at
- message send and method-return time ....
- If you add a message send between the compare and the decrement,
- things will go mad ... (especially be careful when adding a debugPrint-here)
- "
- count ~~ 0 ifTrue:[
+ count > 0 ifTrue:[
^ self
].
@@ -594,7 +652,7 @@
Thus, the count is not always non-zero after returning from
suspend.
"
- [count == 0] whileTrue:[
+ [count <= 0] whileTrue:[
waitingProcesses add:activeProcess.
"
for some more descriptive info in processMonitor ...
@@ -602,12 +660,12 @@
"
[
activeProcess suspendWithState:#wait
- ] ifCurtailed:[
+ ] valueOnUnwindDo:[
waitingProcesses removeIdentical:activeProcess ifAbsent:[].
wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
].
- count == 0 ifTrue:[
- "/ care for someone manually resuming me ...
+ count <= 0 ifTrue:[
+ "/ care for someone manually resuming me (i.e. semaphore still not avail)...
"/ being multiple times on waitingProcesses
waitingProcesses removeIdentical:activeProcess ifAbsent:[].
]
@@ -649,76 +707,64 @@
|activeProcess timeoutOccured wasBlocked unblock now endTime|
- "
- bad ST/X trick (needs change, when multiProcessor support is added):
- this works only since interrupts are only serviced at
- message send and method-return time ....
- If you add a message send between the compare and the decrement,
- things will go mad ... (especially be careful when adding a debugPrint-here)
- "
- count ~~ 0 ifTrue:[
- count := count - 1.
- count == 0 ifTrue:[
- lastOwnerID := Processor activeProcessId.
- ].
- ^ self
- ].
-
- "
- with zero-timeout, this is a poll
- "
- milliSeconds = 0 ifTrue:[
- ^ nil
- ].
-
- activeProcess := Processor activeProcess.
-
wasBlocked := OperatingSystem blockInterrupts.
- "
- calculate the end-time
- "
- milliSeconds notNil ifTrue:[
- now := OperatingSystem getMillisecondTime.
- endTime := OperatingSystem millisecondTimeAdd:now and:milliSeconds.
-
- unblock := [timeoutOccured := true. Processor resume:activeProcess].
- Processor addTimedBlock:unblock for:activeProcess atMilliseconds:endTime.
- ].
+ count <= 0 ifTrue:[
+ "
+ with zero-timeout, this is a poll
+ "
+ milliSeconds = 0 ifTrue:[
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ^ nil
+ ].
- "
- need a while-loop here, since more than one process may
- wait for it and another one may also wake up.
- Thus, the count is not always non-zero after returning from
- suspend.
- "
- [count == 0] whileTrue:[
- waitingProcesses add:activeProcess.
+ activeProcess := Processor activeProcess.
- timeoutOccured := false.
"
- for some more descriptive info in processMonitor ...
- ... set the state to #wait (instead of #suspend)
+ calculate the end-time
"
- [
- activeProcess suspendWithState:#wait.
- ] ifCurtailed:[
- unblock := nil.
- waitingProcesses removeIdentical:activeProcess ifAbsent:[].
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ milliSeconds notNil ifTrue:[
+ now := OperatingSystem getMillisecondTime.
+ endTime := OperatingSystem millisecondTimeAdd:now and:milliSeconds.
+
+ unblock := [timeoutOccured := true. Processor resume:activeProcess].
+ Processor addTimedBlock:unblock for:activeProcess atMilliseconds:endTime.
].
- waitingProcesses removeIdentical:activeProcess ifAbsent:[].
- timeoutOccured ifTrue:[
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- unblock := nil.
- ^ nil
+ "
+ need a while-loop here, since more than one process may
+ wait for it and another one may also wake up.
+ Thus, the count is not always non-zero after returning from
+ suspend.
+ "
+ [count <= 0] whileTrue:[
+ waitingProcesses add:activeProcess.
+
+ timeoutOccured := false.
+ "
+ for some more descriptive info in processMonitor ...
+ ... set the state to #wait (instead of #suspend)
+ "
+ [
+ activeProcess suspendWithState:#wait.
+ ] valueOnUnwindDo:[
+ unblock := nil.
+ waitingProcesses removeIdentical:activeProcess ifAbsent:[].
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ].
+
+ waitingProcesses removeIdentical:activeProcess ifAbsent:[].
+ timeoutOccured ifTrue:[
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ unblock := nil.
+ ^ nil
+ ].
].
- ].
- unblock notNil ifTrue:[
- Processor removeTimedBlock:unblock.
- unblock := nil.
+ unblock notNil ifTrue:[
+ Processor removeTimedBlock:unblock.
+ unblock := nil.
+ ].
].
count := count - 1.
@@ -735,5 +781,5 @@
!Semaphore class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic/Semaphore.st,v 1.62 2002-07-09 13:58:58 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Semaphore.st,v 1.63 2002-07-10 11:10:24 cg Exp $'
! !