On Sat, 11 Jan 2020 at 18:01, ducasse <stepharo@netcourrier.com> wrote:
On 11 Jan 2020, at 10:50, Ben Coman <btc@openinworld.com> wrote:
On Sat, 11 Jan 2020 at 06:31, Sven Van Caekenberghe <sven@stfx.eu> 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.
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.
Indeed. I will add this note to my chapter.
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'
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.
Yes I realize it.
Not needing to trace "Original process" in the first example gives less for the reader to digest
Yes I thought the same. I thought that I could have the following strategy. Give a first simple version, them revisiting it after.
At first glance I thought using #fork would a simpler example than using #forkAt:, but the former interacts with the implicit priority of existing UI process, while the latter is explicit and so actually makes a simpler example. cheers -ben