123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 2005,2009 by the Free Pascal development team
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$ifdef FPC_HAS_FEATURE_THREADING}
- constructor TSimpleRWSync.Create;
- begin
- System.InitCriticalSection(Crit);
- end;
- destructor TSimpleRWSync.Destroy;
- begin
- System.DoneCriticalSection(Crit);
- end;
- function TSimpleRWSync.Beginwrite : boolean;
- begin
- System.EnterCriticalSection(Crit);
- result:=true;
- end;
- procedure TSimpleRWSync.Endwrite;
- begin
- System.LeaveCriticalSection(Crit);
- end;
- procedure TSimpleRWSync.Beginread;
- begin
- System.EnterCriticalSection(Crit);
- end;
- procedure TSimpleRWSync.Endread;
- begin
- System.LeaveCriticalSection(Crit);
- end;
- constructor TMultiReadExclusiveWriteSynchronizer.Create;
- begin
- System.InitCriticalSection(fwritelock);
- fwaitingwriterlock:=RTLEventCreate;
- RTLEventResetEvent(fwaitingwriterlock);
- { 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);
- fwritelocked:=0;
- freaderqueue:=BasicEventCreate(nil,true,false,'');
- { synchronize initialization with later reads/writes }
- ReadWriteBarrier;
- end;
- destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
- begin
- { synchronize destruction with previous endread/write operations }
- ReadWriteBarrier;
- System.DoneCriticalSection(fwritelock);
- RtlEventDestroy(fwaitingwriterlock);
- BasicEventDestroy(freaderqueue);
- end;
- function TMultiReadExclusiveWriteSynchronizer.Beginwrite : boolean;
- begin
- { wait for any other writers that may be in progress }
- System.EnterCriticalSection(fwritelock);
- { it is possible that earlier on we missed waiting on the
- fwaitingwriterlock and that it's still set (must be done
- after acquiring the fwritelock, because otherwise one
- writer could reset the fwaitingwriterlock of another one
- that's about to wait on it) }
- RTLeventResetEvent(fwaitingwriterlock);
- { new readers have to block from now on; writers get priority to avoid
- writer starvation (since they have to compete with potentially many
- concurrent readers and other writers) }
- BasicEventResetEvent(freaderqueue);
- { for quick checking by candidate-readers -- use interlockedincrement/
- decrement instead of setting 1/0, because a single thread can
- recursively acquire the write lock multiple times }
- System.InterlockedIncrement(fwritelocked);
- { order increment vs freadercount check }
- ReadWriteBarrier;
- { 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 }
- while freadercount<>0 do
- RTLEventWaitFor(fwaitingwriterlock);
- { Make sure that out-of-order execution cannot already perform reads
- inside the critical section before the lock has been acquired }
- ReadBarrier;
- { we have the writer lock, and all readers are gone }
- result:=true;
- end;
- procedure TMultiReadExclusiveWriteSynchronizer.Endwrite;
- begin
- { Finish all writes inside the section so that everything executing
- afterwards will certainly see those results }
- WriteBarrier;
- { signal potential readers that the coast is clear if all recursive
- write locks have been freed }
- if System.InterlockedDecrement(fwritelocked)=0 then
- begin
- { wake up waiting readers (if any); do not check first whether freadercount
- is <> 0, because the InterlockedDecrement in the while loop of BeginRead
- can have already occurred, so a single reader may be about to wait on
- freaderqueue even though freadercount=0. Setting an event multiple times
- is no problem. }
- BasicEventSetEvent(freaderqueue);
- end;
- { free the writer lock so another writer can become active }
- System.LeaveCriticalSection(fwritelock);
- end;
- procedure TMultiReadExclusiveWriteSynchronizer.Beginread;
- Const
- wrSignaled = 0;
- wrTimeout = 1;
- wrAbandoned= 2;
- wrError = 3;
- begin
- System.InterlockedIncrement(freadercount);
- { order vs fwritelocked check }
- ReadWriteBarrier;
- { wait until there is no more writer }
- while fwritelocked<>0 do
- begin
- { 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
- 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
- RTLEventSetEvent(fwaitingwriterlock);
- if (BasicEventWaitFor(high(cardinal),freaderqueue) in [wrAbandoned,wrError]) then
- raise Exception.create('BasicEventWaitFor failed in TMultiReadExclusiveWriteSynchronizer.Beginread');
- { and try again: first increase freadercount, only then check
- fwritelocked }
- System.InterlockedIncrement(freadercount);
- { order vs fwritelocked check at the start of the loop }
- ReadWriteBarrier;
- end;
- end;
- procedure TMultiReadExclusiveWriteSynchronizer.Endread;
- 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
- 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
- order. }
- if System.InterlockedDecrement(freadercount)=0 then
- begin
- { order fwritelocked check vs freadercount modification }
- ReadWriteBarrier;
- if fwritelocked<>0 then
- RTLEventSetEvent(fwaitingwriterlock);
- end;
- end;
- {$endif FPC_HAS_FEATURE_THREADING}
|