For greater visibility and comprehension, it might be useful to early in the manual define a utility method...
Object>>crTracePriority
self crTrace: '[', Processor activePriority printString, ']', self printString
So you need to add code to your example to be able to determine what will happen. The easiest thing would be to delay long enough that both can run. 1 millisecond is more than enough.
This is a good point. It may be useful for the example to be expanded to...
| semaphore p1 p2 |
semaphore := Semaphore new.
p1 := [ semaphore wait.
'Process 1' crTracePriority ] fork.
p2 := [semaphore signal.
'Process 2' crTracePriority ] fork.
'Original process pre-yield' crTracePriority .
1 milliSeconds wait.
'Original process post-yield' crTracePriority .
which would produce==>
[40]'Original process pre-yield'
[40]'Process 2'
[40]'Process 1'
[40]'Original process post-yield'
with other examples producing...
[40]'Original process pre-yield'
[30]'Process 1'
[20]'Process 2'
[40]'Original process post-yield'
[60]'Process 2'
[50]'Process 1'
[40]'Original process pre-yield'
[40]'Original process post-yield'
Now this sentence "The semaphore takes the first waiting process (==p1==) and reschedule it by adding it to the end of the suspended lists.��� is super naive. Is the semaphore signalling scheduled? or not?
I would say these three things, something like this:
"A semaphore is a queue (implemented as a linked list) and an excess signals count, which is a non-negative integer. On instance creation a new semaphore is empty and has a zero excess signals count. A semaphore created for mutual exclusion is empty and has an excess signals count of one."
"When a process waits on a semaphore, if the semaphore's excess signals count is non-zero, then the excess signal count is decremented, and the process proceeds. But if the semaphore has a zero excess signals count then the process is unscheduled and added to the end of the semaphore, after any other processes that are queued on the semaphore."
"When a semaphore is signaled, if it is not empty, the first process is removed from it and added to the runnable processes in the scheduler. If the semaphore is empty its excess signals count is incremented.
Given these three statements it is easy to see how they work, how to use them for mutual exclusion, etc.
signal
"Primitive. Send a signal through the receiver. If one or more processes
have been suspended trying to receive a signal, allow the first one to
proceed. If no process is waiting, remember the excess signal. Essential.
See Object documentation whatIsAPrimitive."
<primitive: 85>
self primitiveFailed
"self isEmpty
ifTrue: [excessSignals := excessSignals+1]
ifFalse: [Processor resume: self removeFirstLink]"
I wanted to know what is really happening when a semaphore is signalled.
Now resume: does not exist on Processor.
I will look in the VM code.
For quick reference, here is some relevant VM code (the StackInterpreter code is a little simpler...)
StackInterpreter class >> initializePrimitiveTable [
...
"Control Primitives (80-89)"
(85 primitiveSignal)
(86 primitiveWait)
...
]
InterpreterPrimitives >> primitiveWait [
| sema excessSignals activeProc |
sema := self stackTop. "rcvr"
excessSignals := self fetchInteger: ExcessSignalsIndex ofObject: sema.
excessSignals > 0
ifTrue:
[self storeInteger: ExcessSignalsIndex ofObject: sema withValue: excessSignals - 1]
ifFalse:
[activeProc := self activeProcess.
self addLastLink: activeProc toList: sema.
self transferTo: self wakeHighestPriority]
]
InterpreterPrimitives >> primitiveSignal [
"Synchronously signal the semaphore.
This may change the active process as a result."
self synchronousSignal: self stackTop
]
StackInterpreter >> synchronousSignal: aSemaphore [
"Signal the given semaphore from within the interpreter.
Answer if the current process was preempted."
| excessSignals |
<inline: false>
(self isEmptyList: aSemaphore) ifTrue:
["no process is waiting on this semaphore"
excessSignals := self fetchInteger: ExcessSignalsIndex ofObject: aSemaphore.
self storeInteger: ExcessSignalsIndex
ofObject: aSemaphore
withValue: excessSignals + 1.
^false].
objectMemory ensureSemaphoreUnforwardedThroughContext: aSemaphore.
^self resume: (self removeFirstLinkOfList: aSemaphore)
preemptedYieldingIf: preemptionYields
]
cheers -ben