mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-19 10:02:34 +02:00

atomic operations so that in case it's created while multiple threads are already running, all threads are guaranteed to see this initialisation * some minor changes to the comments of TMREWS git-svn-id: trunk@15074 -
167 lines
5.8 KiB
PHP
167 lines
5.8 KiB
PHP
{
|
|
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;
|