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