3.11.5. Task mem array synchronization¶
During a task update, the only memory slot that is written to is task_t%new
,
while potentially all memory slots are needed to produce the new values.
Before a task update, all memory slots iit(1:nt-1)
of nbor (source) tasks may in
principle be needed, when computing guard zone values for the (target) task that
the thread is going to update.
If the nbor task is dormant (with bits%busy
not set), it could possibly start
computing values after its time slot info has been acquired, but since the
download for a single task takes less than 1% of the update, there is no chance
whatsoever that the task has time to both finish one update, and start producing
new mem array values for the next time slot (number 1 in terms of the iit array).
If the task is active (with bits%busy
set), and is just about to finish its
update, it might have time to do that, changing in the process its time slot
indices and its task_t%time, but with no chance to finish one more update.
Hence, after retrieving (atomically) the time slot information from a source task,
using the task_t%timeslots()
procedure, the thread working on computing guard
zone values for the target task can be sure that it can access slots (1:nt-1)
without conflicting with updates to the source task.
The only possible exception would be if the task was suspended in the middle
of the download_t%different()
or download_t%same()
procedure call, and did not
wake up until the source task had updated several times. This might possibly
(but still with very low probability) happen if one uses hyperthreading with a
large factor (such as using 20 threads on 2 cores).
It is important, however, to make sure that the source%t
and souce%iit
values
are retrieved only once, and that they are consistent, but if the local iit()
values are based on a source%it
that is updated as the last thing in task_t%rotate()
,
then even if the slot information is being changed by some thread executing
source%rotate()
during the target%timeslots() call, the target will still
receive consistent iit(1:nt-1)
memory slot values.
Consider the situation before the source update finishes::
iit(1) iit(2) iit(3) iit(4) iit(5)
4 5 1 2 3
(it) (new)
t(1) t(2) t(3) t(4) t(5)
0.3 0.4 0.0 0.1 0.2
(1) (it) (new) (4) (5)
All that happens when the source update finishes is that t(3)
is set to 0.5,
the new
mem slot is filled with updated variable values, and that the iit(:)
array is shifted left, so it reads:
iit(1) iit(2) iit(3) iit(4) iit(5)
5 1 2 3 4
(it) (new)
The shift of iit(:) may be replicated by knowing only the value of it
, which
is set atomically. So, if it
is incremented (cyclically) right after the
time slot that it corresponds to has been updated, then a thread downloading
to a target gets a set of times to interpolate between that is consistent with
the content of the corresponding memory slots. Should the thread pick up it
a microsecond too early it still gets useful data, since already the check_ready
test that happened typically a long time ago made the judgement that the task
was ready to be updated.