mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 17:51:38 +01:00 
			
		
		
		
	 d5f415b047
			
		
	
	
		d5f415b047
		
	
	
	
	
		
			
			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;
 |