Thanks ben, this is a nice idea to illustrate what is happening I will use it. 
Thanks for the VM code :). 
I like it too. 

I will add a note also mentioning that indeed there is a process to execute the 
snippets that the reader will write and execute :). 


Stef

PS: what makes me laugh a bit is that people talk about documentation when 
there are nearly none on the topics on which I write. 
I write to be able to fully forget everything and free my brain. I remember 
writing on bloc, exceptions, when there was only obscure  
texts or none as if mail discussions would make a book. At least this is not 
the level I want. 
To me I would like to have a book like Smalltalk and its implementation but 
about Pharo. 
This was a book! So I will write it piece by piece. 



> 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  
> 
> On Fri, 10 Jan 2020 at 13:13, Eliot Miranda <[email protected] 
> <mailto:[email protected]>> wrote:
> 
> 
> On Thu, Jan 9, 2020 at 5:03 AM ducasse <[email protected] 
> <mailto:[email protected]>> wrote:
> Hi
> 
> I wanted to explain
> 
> | semaphore p1 p2 |
> semaphore := Semaphore new.
> p1 := [ semaphore wait.
>         'p1' crTrace ] fork.
> 
> p2 := [semaphore signal.
>          'p2' crTrace ] fork.
> 
> displays p2 and p1. 
> but I would like explain clearly but it depends on the semantics of signal. 
> 
> 
> - ==p1== is scheduled and its execution starts to wait on the semaphore, so 
> it is removed from the run queue of the scheduler and added to the waiting 
> list of the semaphore.
> - ==p2== is scheduled and it signals the semaphore. The semaphore takes the 
> first waiting process (==p1==) and reschedule it by adding it to the end of 
> the suspended lists.
> 
> Since Smalltalk does not have a preemptive scheduler, neither p1 nor p2 will 
> start to run until something else happens after the execution of p1 := [...] 
> fork. p2 := [...] fork. So for example, if there is Processor yield then p1 
> can start to run.
> 
> 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...)
> 
> https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/StackInterpreter.class.st#L1422-L1432
>  
> <https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/StackInterpreter.class.st#L1422-L1432>
>   
> StackInterpreter class >> initializePrimitiveTable [
> ...
>     "Control Primitives (80-89)"
>     (85 primitiveSignal)
>     (86 primitiveWait)
> ...
> ]
> 
> https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/InterpreterPrimitives.class.st#L5385-L5396
>  
> <https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/InterpreterPrimitives.class.st#L5385-L5396>
>   
> 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]
> ]
> 
> 
> https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/InterpreterPrimitives.class.st#L4045-L4049
>  
> <https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/InterpreterPrimitives.class.st#L4045-L4049>
>   
> InterpreterPrimitives >> primitiveSignal [
>     "Synchronously signal the semaphore.
>      This may change the active process as a result."
>     self synchronousSignal: self stackTop
> ]
> 
> 
> https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/StackInterpreter.class.st#L21662-L21679
>  
> <https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/pharo/headless/smalltalksrc/VMMaker/StackInterpreter.class.st#L21662-L21679>
>   
> 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

Reply via email to