Hi Ben,
> On 11 Jan 2020, at 10:50, Ben Coman <[email protected]> wrote:
>
>
>
> On Sat, 11 Jan 2020 at 06:31, Sven Van Caekenberghe <[email protected]> wrote:
> Hi Ben,
>
> Great approach, though I would make one change to make your example
> completely copy/paste runnable.
>
> Stef's original example:
>
> | trace semaphore p1 p2 |
>
> semaphore := Semaphore new.
>
> trace := [ :message |
> ('[{1}] {2}' format: { Processor activeProcess priority. message })
> crLog ].
>
> p1 := [
> semaphore wait.
> trace value: 'Process 1' ] fork.
>
> p2 := [
> semaphore signal.
> trace value: 'Process 2' ] fork.
>
> trace value: 'Original process pre-yield'.
> Processor yield.
> trace value: 'Original process post-yield'.
>
> Gives:
>
> '[40] Original process pre-yield'
> '[40] Process 2'
> '[40] Original process post-yield'
> '[40] Process 1'
>
> But not running the yield section gives:
>
> '[40] Process 2'
> '[40] Process 1'
>
> which is an identical result if the 'Original process' traces are filtered
> out.
>
>
> From this it would seem that the code in p2 continues after signal and only
> later does p1 get past its wait.
>
> Yes, a #signal does not transfer execution unless the waiting-process that
> received the signal is a higher priority.
> Within the same priority, it just makes waiting-process runnable, and the
> highest-priority-runnable-process is the one that is run.
OK, I can understand that, the question remains what happens when the processes
have equal priorities.
> Playing with the priorities we can change that order (apparently);
>
> | trace semaphore p1 p2 |
>
> semaphore := Semaphore new.
>
> trace := [ :message |
> ('[{1}] {2}' format: { Processor activeProcess priority. message })
> crLog ].
>
> p1 := [
> semaphore wait.
> trace value: 'Process 1' ] forkAt: 30.
>
> p2 := [
> semaphore signal.
> trace value: 'Process 2' ] forkAt: 20.
>
> Gives:
>
> '[30] Process 1'
> '[20] Process 2'
>
> Again, the yield section makes no difference. So something else happened.
>
> The yield made no difference because it only facilitates other processes
> at-the-SAME-priority getting a chance to run.
> Yield doesn't put the current-process to sleep, it just moves the process to
> the back of its-priority-runQueue. It gets to run again before any lower
> priority process gets a chance to run.
>
> Yielding will never allow a lower-priority-process to run.
> For a lower-priority process to run, the current-process needs to sleep
> rather than yield.
These are clear statements.
> Compare...
> | trace semaphore p1 p2 |
> semaphore := Semaphore new.
> trace := [ :message | ('@{1} {2}' format: { Processor activePriority. message
> }) crLog ].
> p1 := [
> trace value: 'Process 1a waits for signal on semaphore'.
> semaphore wait.
> trace value: 'Process 1b received signal' ] forkAt: 30.
> p2 := [
> trace value: 'Process 2a signals semaphore'.
> semaphore signal.
> trace value: 'Process 2b continues' ] forkAt: 20.
> trace value: 'Original process pre-yield'.
> Processor yield.
> trace value: 'Original process post-yield'.
>
> ==>
> '@40 Original process pre-yield'
> '@40 Original process post-yield'
> '@30 Process 1a waits for signal on semaphore'
> '@20 Process 2a signals semaphore'
> '@30 Process 1b received signal'
> '@20 Process 2b continues'
>
> with...
> | trace semaphore p1 p2 |
> semaphore := Semaphore new.
> trace := [ :message | ('@{1} {2}' format: { Processor activePriority. message
> }) crLog ].
> p1 := [
> trace value: 'Process 1a waits for signal on semaphore'.
> semaphore wait.
> trace value: 'Process 1b received signal' ] forkAt: 30.
> p2 := [
> trace value: 'Process 2a signals semaphore'.
> semaphore signal.
> trace value: 'Process 2b continues' ] forkAt: 20.
> trace value: 'Original process pre-delay'.
> 1 milliSecond wait.
> trace value: 'Original process post-delay'.
>
> ==>
> '@40 Original process pre-delay'
> '@30 Process 1a waits for signal on semaphore'
> '@20 Process 2a signals semaphore'
> '@30 Process 1b received signal'
> '@20 Process 2b continues'
> '@40 Original process post-delay'
OK, good example: I think/hope I understand.
Now, these further examples only strengthen my believe that it is simply
impossible to talk about semaphores without talking about (the complexities) of
process scheduling.
Semaphores exist as a means to coordinate processes, hence when using them you
have to understand what (will) happen, and apparently that is quite complex.
In any case, thanks again for the explanations,
Sven
> Stef, on further consideration I think your first examples should not-have p1
> and p2 the same priority.
> Scheduling of same-priority processes and how they interact with the UI
> thread is an extra level of complexity that may be better done shortly after.
> Not needing to trace "Original process" in the first example gives less for
> the reader to digest
>
> So your first example might compare...
> | trace semaphore p1 p2 |
> semaphore := Semaphore new.
> trace := [ :message | ('@{1} {2}' format: { Processor activePriority. message
> }) crLog ].
> p1 := [
> trace value: 'Process 1a waits for signal on semaphore'.
> semaphore wait.
> trace value: 'Process 1b received signal' ] forkAt: 20.
> p2 := [
> trace value: 'Process 2a signals semaphore'.
> semaphore signal.
> trace value: 'Process 2b continues' ] forkAt: 30.
>
> ==>
> '@30 Process 1a waits for signal on semaphore'
> '@20 Process 2a signals semaphore'
> '@30 Process 1b received signal'
> '@20 Process 2b continues'
>
>
> with the priority order swapped...
> | trace semaphore p1 p2 |
> semaphore := Semaphore new.
> trace := [ :message | ('@{1} {2}' format: { Processor activePriority. message
> }) crLog ].
> p1 := [
> trace value: 'Process 1a waits for signal on semaphore'.
> semaphore wait.
> trace value: 'Process 1b received signal' ] forkAt: 30.
> p2 := [
> trace value: 'Process 2a signals semaphore'.
> semaphore signal.
> trace value: 'Process 2b continues' ] forkAt: 20.
>
> ==>
> '@30 Process 2a signals semaphore'
> '@30 Process 2b continues'
> '@20 Process 1a waits for signal on semaphore'
> '@20 Process 1b received signal'
>
>
> cheers -ben