Browse Source

* properly order all operations in TMultiReadExclusiveWriteSynchronizer
using memory barriers, rather than half-heartedly using atomic
operations. Fixes random failures in trwsync on POWER7 processors
(and probably other weakly ordered architectures)

git-svn-id: trunk@27723 -

Jonas Maebe 11 năm trước cách đây
mục cha
commit
35c64a7a69
1 tập tin đã thay đổi với 36 bổ sung20 xóa
  1. 36 20
      rtl/objpas/sysutils/sysuthrd.inc

+ 36 - 20
rtl/objpas/sysutils/sysuthrd.inc

@@ -50,17 +50,21 @@ begin
   System.InitCriticalSection(fwritelock);
   System.InitCriticalSection(fwritelock);
   fwaitingwriterlock:=RTLEventCreate;
   fwaitingwriterlock:=RTLEventCreate;
   RTLEventResetEvent(fwaitingwriterlock);
   RTLEventResetEvent(fwaitingwriterlock);
-  { make sure all threads see the initialisation of fwritelock and
-    freadercount (we only use atomic operation further on as well) }
+  { atomic initialisation because BeginRead does not contain a memory barrier
+    before it modifies freadercount (with an atomic operation), so we have
+    to ensure that it sees this initial value }
   System.InterlockedExchange(freadercount,0);
   System.InterlockedExchange(freadercount,0);
-  System.InterlockedExchange(fwritelocked,0);
+  fwritelocked:=0;
   freaderqueue:=BasicEventCreate(nil,true,false,'');
   freaderqueue:=BasicEventCreate(nil,true,false,'');
+  { synchronize initialization with later reads/writes }
+  ReadWriteBarrier;
 end;
 end;
 
 
 
 
 destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
 destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
 begin
 begin
-  System.InterlockedExchange(fwritelocked,0);
+  { synchronize destruction with previous endread/write operations }
+  ReadWriteBarrier;
   System.DoneCriticalSection(fwritelock);
   System.DoneCriticalSection(fwritelock);
   RtlEventDestroy(fwaitingwriterlock);
   RtlEventDestroy(fwaitingwriterlock);
   BasicEventDestroy(freaderqueue);
   BasicEventDestroy(freaderqueue);
@@ -85,13 +89,13 @@ begin
     decrement instead of setting 1/0, because a single thread can
     decrement instead of setting 1/0, because a single thread can
     recursively acquire the write lock multiple times }
     recursively acquire the write lock multiple times }
   System.InterlockedIncrement(fwritelocked);
   System.InterlockedIncrement(fwritelocked);
+  { order increment vs freadercount check }
+  ReadWriteBarrier;
 
 
-  { wait until all readers are gone -- freadercount and fwritelocked are only
-    accessed using atomic operations (that's why we use
-    InterLockedExchangeAdd(x,0) below) -> always in-order. The writer always
-    first sets fwritelocked and then checks freadercount, while the readers
+  { wait until all readers are gone. The writer always first sets
+    fwritelocked and then checks freadercount, while the readers
     always first increase freadercount and then check fwritelocked }
     always first increase freadercount and then check fwritelocked }
-  while (System.InterLockedExchangeAdd(freadercount,0)<>0) do
+  while freadercount<>0 do
     RTLEventWaitFor(fwaitingwriterlock);
     RTLEventWaitFor(fwaitingwriterlock);
 
 
   { Make sure that out-of-order execution cannot already perform reads
   { Make sure that out-of-order execution cannot already perform reads
@@ -106,7 +110,7 @@ end;
 procedure  TMultiReadExclusiveWriteSynchronizer.Endwrite;
 procedure  TMultiReadExclusiveWriteSynchronizer.Endwrite;
 begin
 begin
   { Finish all writes inside the section so that everything executing
   { Finish all writes inside the section so that everything executing
-    afterwards will certainly see these results }
+    afterwards will certainly see those results }
   WriteBarrier;
   WriteBarrier;
 
 
   { signal potential readers that the coast is clear if all recursive
   { signal potential readers that the coast is clear if all recursive
@@ -133,12 +137,16 @@ Const
   wrError    = 3;
   wrError    = 3;
 begin
 begin
   System.InterlockedIncrement(freadercount);
   System.InterlockedIncrement(freadercount);
+  { order vs fwritelocked check }
+  ReadWriteBarrier;
   { wait until there is no more writer }
   { wait until there is no more writer }
-  while System.InterLockedExchangeAdd(fwritelocked,0)<>0 do
+  while fwritelocked<>0 do
     begin
     begin
       { there's a writer busy or wanting to start -> wait until it's
       { there's a writer busy or wanting to start -> wait until it's
         finished; a writer may already be blocked in the mean time, so
         finished; a writer may already be blocked in the mean time, so
-        wake it up if we're the last to go to sleep }
+        wake it up if we're the last to go to sleep -- order frwritelocked 
+        check above vs freadercount check/modification below }
+      ReadWriteBarrier;
       if System.InterlockedDecrement(freadercount)=0 then
       if System.InterlockedDecrement(freadercount)=0 then
         RTLEventSetEvent(fwaitingwriterlock);
         RTLEventSetEvent(fwaitingwriterlock);
       if (BasicEventWaitFor(high(cardinal),freaderqueue) in [wrAbandoned,wrError]) then
       if (BasicEventWaitFor(high(cardinal),freaderqueue) in [wrAbandoned,wrError]) then
@@ -146,23 +154,31 @@ begin
       { and try again: first increase freadercount, only then check
       { and try again: first increase freadercount, only then check
         fwritelocked }
         fwritelocked }
       System.InterlockedIncrement(freadercount);
       System.InterlockedIncrement(freadercount);
+     { order vs fwritelocked check at the start of the loop }
+      ReadWriteBarrier;
     end;
     end;
-  { Make sure that out-of-order execution cannot perform reads
-    inside the critical section before the lock has been acquired }
-  ReadBarrier;
 end;
 end;
 
 
 
 
 procedure  TMultiReadExclusiveWriteSynchronizer.Endread;
 procedure  TMultiReadExclusiveWriteSynchronizer.Endread;
 begin
 begin
+  { order freadercount decrement to all operations in the critical section 
+    (otherwise, freadercount could become zero in another thread/cpu before
+     all reads in the protected section were committed, so a writelock could
+     become active and change things that we will still see) }
+  ReadWriteBarrier;
   { If no more readers, wake writer in the ready-queue if any. Since a writer
   { If no more readers, wake writer in the ready-queue if any. Since a writer
-    always first atomically sets fwritelocked and then atomically checks the
-    freadercount, first modifying freadercount and then checking fwritelock
+    always first sets fwritelocked and then checks the freadercount (with a
+    barrier in between) first modifying freadercount and then checking fwritelock
     ensures that we cannot miss one of the events regardless of execution
     ensures that we cannot miss one of the events regardless of execution
     order. }
     order. }
-  if (System.InterlockedDecrement(freadercount)=0) and
-     (System.InterLockedExchangeAdd(fwritelocked,0)<>0) then
-    RTLEventSetEvent(fwaitingwriterlock);
+  if System.InterlockedDecrement(freadercount)=0 then
+    begin
+      { order fwritelocked check vs freadercount modification }
+      ReadWriteBarrier;
+      if fwritelocked<>0 then
+        RTLEventSetEvent(fwaitingwriterlock);
+    end;
 end;
 end;
 {$endif FPC_HAS_FEATURE_THREADING}
 {$endif FPC_HAS_FEATURE_THREADING}