sysuthrd.inc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2005,2009 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {$ifdef FPC_HAS_FEATURE_THREADING}
  11. constructor TSimpleRWSync.Create;
  12. begin
  13. System.InitCriticalSection(Crit);
  14. end;
  15. destructor TSimpleRWSync.Destroy;
  16. begin
  17. System.DoneCriticalSection(Crit);
  18. end;
  19. function TSimpleRWSync.Beginwrite : boolean;
  20. begin
  21. System.EnterCriticalSection(Crit);
  22. result:=true;
  23. end;
  24. procedure TSimpleRWSync.Endwrite;
  25. begin
  26. System.LeaveCriticalSection(Crit);
  27. end;
  28. procedure TSimpleRWSync.Beginread;
  29. begin
  30. System.EnterCriticalSection(Crit);
  31. end;
  32. procedure TSimpleRWSync.Endread;
  33. begin
  34. System.LeaveCriticalSection(Crit);
  35. end;
  36. constructor TMultiReadExclusiveWriteSynchronizer.Create;
  37. begin
  38. System.InitCriticalSection(fwritelock);
  39. fwaitingwriterlock:=RTLEventCreate;
  40. RTLEventResetEvent(fwaitingwriterlock);
  41. { atomic initialisation because BeginRead does not contain a memory barrier
  42. before it modifies freadercount (with an atomic operation), so we have
  43. to ensure that it sees this initial value }
  44. System.InterlockedExchange(freadercount,0);
  45. fwritelocked:=0;
  46. freaderqueue:=BasicEventCreate(nil,true,false,'');
  47. { synchronize initialization with later reads/writes }
  48. ReadWriteBarrier;
  49. end;
  50. destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
  51. begin
  52. { synchronize destruction with previous endread/write operations }
  53. ReadWriteBarrier;
  54. System.DoneCriticalSection(fwritelock);
  55. RtlEventDestroy(fwaitingwriterlock);
  56. BasicEventDestroy(freaderqueue);
  57. end;
  58. function TMultiReadExclusiveWriteSynchronizer.Beginwrite : boolean;
  59. begin
  60. { wait for any other writers that may be in progress }
  61. System.EnterCriticalSection(fwritelock);
  62. { it is possible that earlier on we missed waiting on the
  63. fwaitingwriterlock and that it's still set (must be done
  64. after acquiring the fwritelock, because otherwise one
  65. writer could reset the fwaitingwriterlock of another one
  66. that's about to wait on it) }
  67. RTLeventResetEvent(fwaitingwriterlock);
  68. { new readers have to block from now on; writers get priority to avoid
  69. writer starvation (since they have to compete with potentially many
  70. concurrent readers and other writers) }
  71. BasicEventResetEvent(freaderqueue);
  72. { for quick checking by candidate-readers -- use interlockedincrement/
  73. decrement instead of setting 1/0, because a single thread can
  74. recursively acquire the write lock multiple times }
  75. System.InterlockedIncrement(fwritelocked);
  76. { order increment vs freadercount check }
  77. ReadWriteBarrier;
  78. { wait until all readers are gone. The writer always first sets
  79. fwritelocked and then checks freadercount, while the readers
  80. always first increase freadercount and then check fwritelocked }
  81. while freadercount<>0 do
  82. RTLEventWaitFor(fwaitingwriterlock);
  83. { Make sure that out-of-order execution cannot already perform reads
  84. inside the critical section before the lock has been acquired }
  85. ReadBarrier;
  86. { we have the writer lock, and all readers are gone }
  87. result:=true;
  88. end;
  89. procedure TMultiReadExclusiveWriteSynchronizer.Endwrite;
  90. begin
  91. { Finish all writes inside the section so that everything executing
  92. afterwards will certainly see those results }
  93. WriteBarrier;
  94. { signal potential readers that the coast is clear if all recursive
  95. write locks have been freed }
  96. if System.InterlockedDecrement(fwritelocked)=0 then
  97. begin
  98. { wake up waiting readers (if any); do not check first whether freadercount
  99. is <> 0, because the InterlockedDecrement in the while loop of BeginRead
  100. can have already occurred, so a single reader may be about to wait on
  101. freaderqueue even though freadercount=0. Setting an event multiple times
  102. is no problem. }
  103. BasicEventSetEvent(freaderqueue);
  104. end;
  105. { free the writer lock so another writer can become active }
  106. System.LeaveCriticalSection(fwritelock);
  107. end;
  108. procedure TMultiReadExclusiveWriteSynchronizer.Beginread;
  109. Const
  110. wrSignaled = 0;
  111. wrTimeout = 1;
  112. wrAbandoned= 2;
  113. wrError = 3;
  114. begin
  115. System.InterlockedIncrement(freadercount);
  116. { order vs fwritelocked check }
  117. ReadWriteBarrier;
  118. { wait until there is no more writer }
  119. while fwritelocked<>0 do
  120. begin
  121. { there's a writer busy or wanting to start -> wait until it's
  122. finished; a writer may already be blocked in the mean time, so
  123. wake it up if we're the last to go to sleep -- order frwritelocked
  124. check above vs freadercount check/modification below }
  125. ReadWriteBarrier;
  126. if System.InterlockedDecrement(freadercount)=0 then
  127. RTLEventSetEvent(fwaitingwriterlock);
  128. if (BasicEventWaitFor(high(cardinal),freaderqueue) in [wrAbandoned,wrError]) then
  129. raise Exception.create('BasicEventWaitFor failed in TMultiReadExclusiveWriteSynchronizer.Beginread');
  130. { and try again: first increase freadercount, only then check
  131. fwritelocked }
  132. System.InterlockedIncrement(freadercount);
  133. { order vs fwritelocked check at the start of the loop }
  134. ReadWriteBarrier;
  135. end;
  136. end;
  137. procedure TMultiReadExclusiveWriteSynchronizer.Endread;
  138. begin
  139. { order freadercount decrement to all operations in the critical section
  140. (otherwise, freadercount could become zero in another thread/cpu before
  141. all reads in the protected section were committed, so a writelock could
  142. become active and change things that we will still see) }
  143. ReadWriteBarrier;
  144. { If no more readers, wake writer in the ready-queue if any. Since a writer
  145. always first sets fwritelocked and then checks the freadercount (with a
  146. barrier in between) first modifying freadercount and then checking fwritelock
  147. ensures that we cannot miss one of the events regardless of execution
  148. order. }
  149. if System.InterlockedDecrement(freadercount)=0 then
  150. begin
  151. { order fwritelocked check vs freadercount modification }
  152. ReadWriteBarrier;
  153. if fwritelocked<>0 then
  154. RTLEventSetEvent(fwaitingwriterlock);
  155. end;
  156. end;
  157. {$endif FPC_HAS_FEATURE_THREADING}