123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- {
- 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.
- **********************************************************************}
- 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);
- { make sure all threads see the initialisation of fwritelock and
- freadercount (we only use atomic operation further on as well) }
- System.InterlockedExchange(freadercount,0);
- System.InterlockedExchange(fwritelocked,0);
- freaderqueue:=BasicEventCreate(nil,true,false,'');
- end;
- destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
- begin
- System.InterlockedExchange(fwritelocked,0);
- 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);
- { 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
- always first increase freadercount and then check fwritelocked }
- while (System.InterLockedExchangeAdd(freadercount,0)<>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 these 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);
- { wait until there is no more writer }
- while System.InterLockedExchangeAdd(fwritelocked,0)<>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 }
- 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);
- end;
- { Make sure that out-of-order execution cannot perform reads
- inside the critical section before the lock has been acquired }
- ReadBarrier;
- end;
- procedure TMultiReadExclusiveWriteSynchronizer.Endread;
- begin
- { 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
- ensures that we cannot miss one of the events regardless of execution
- order. }
- if (System.InterlockedDecrement(freadercount)=0) and
- (System.InterLockedExchangeAdd(fwritelocked,0)<>0) then
- RTLEventSetEvent(fwaitingwriterlock);
- end;
|