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 <eliot.mira...@gmail.com> wrote:

>
>
> On Thu, Jan 9, 2020 at 5:03 AM ducasse <steph...@netcourrier.com> 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

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

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

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

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