Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2020-08

WriteBarrier

4 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin

Ok, I got a bit further.

Assuming the following
 (and not counting on Interlocked... doing any Barriers itself)

Thread 1
   Foo := val; // normal assign
   WriteBarrier; // make sure above write to Foo is executed, before the
next write to flag
   InterLockedIncrement(Flag);  // needed for the lock, if others do
write access too.
Thread 2
   If InterLockedExchangeAdd(Flag, 0) > 0 then begin
      ReadBarrier; // make sure below read is executed only after the
read of the flag
      myval := Foo;
   end;

That should get the correct value of Foo.
Even if run a million times, and in all of this the read of Flag eventually happens exactly one cpu cycle after it was set.

Now the Examples I've seen all are showing cases where the re-orderable assignment are in close proximity to each other.
On the other hand compiler and CPU are getting better and better. So there is nothing that would in theory stop them to swap read/write order over any amount of statements....

So even if (in Thread 1) I returned from a subroutine (which could be inlined for all I (don't) know), done some other work, and only then set the flag.
If I want to be sure, any value that was written in the subroutine is written before the flag gets set, then I should do a WriteBarrier? Or is that paranoid?

Thanks again
Martin

Reply by email

Jonas Maebe
to Martin
Martin wrote 20 lines
Assuming the following  (and not counting on Interlocked... doing any Barriers itself) Thread 1   Foo := val; // normal assign   WriteBarrier; // make sure above write to Foo is executed, before the next write to flag   InterLockedIncrement(Flag);  // needed for the lock, if others do write access too. Thread 2   If InterLockedExchangeAdd(Flag, 0) > 0 then begin      ReadBarrier; // make sure below read is executed only after the read of the flag      myval := Foo;   end; That should get the correct value of Foo. Even if run a million times, and in all of this the read of Flag eventually happens exactly one cpu cycle after it was set.

Yes.

Show quoted text 5 lines
Now the Examples I've seen all are showing cases where the re-orderable assignment are in close proximity to each other. On the other hand compiler and CPU are getting better and better. So there is nothing that would in theory stop them to swap read/write order over any amount of statements....

Correct. In particular on multi-socket systems, or e.g. Ryzen processors with multiple core/CPU complexes, this is quite possible.

Show quoted text 6 lines
So even if (in Thread 1) I returned from a subroutine (which could be inlined for all I (don't) know), done some other work, and only then set the flag. If I want to be sure, any value that was written in the subroutine is written before the flag gets set, then I should do a WriteBarrier? Or is that paranoid?

It's not paranoid, it is the only correct way to write the code.

Jonas

Reply by email

Martin Frb
to Jonas Maebe
Jonas Maebe wrote 5 lines
On 24/08/2020 19:20, Jonas Maebe via fpc-devel wrote: > It's not paranoid, it is the only correct way to write the code. * if you don't want to use regular synchronisation primitives, like mutexes/signals/events/...

I have events in some cases.

The scenario is the following.
I have workers (TMyWorkerObject) (in threads).
There may bea (maybe huge) number of work items (processed by a few threads).

It can not be predicted in which order the items are going to be collected by the main thread.
So if the work item would *always* have to set an event, each item would need its own event. But I do not want to create hundreds or more of Events (one for each work item)

When the workitem is finished, the main thread may be waiting or not.

- If the main thread is waiting, then it does so by RtlEventWaitFor (on one global event), and the worker will do RtlEventSet.

- The case differs if the main thread is not yet waiting (and also may never do so, if it no longer needs the result / user cancellation). -- In that case the item sets its state to "done". And *before* that does a WriteBarrier.
-- So if the main thread sees that the state is set to done, it knows it does not need an event to wait for. (main thread then does a ReadBarrier)

(state is exchanged interlocked, and the main thread indicates, if it waits for the event. So there is no race condition in exchanging state.)

If the state is "done" there is no waiting (no Event needed), and the worker is no longer accessed in the worker thread (no mutex needed) It seems to me, in this case the Barriers are the easiest way.

Reply by email