| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778 |
- {**************************************************************************************************}
- { }
- { Project JEDI Code Library (JCL) }
- { }
- { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
- { you may not use this file except in compliance with the License. You may obtain a copy of the }
- { License at http://www.mozilla.org/MPL/ }
- { }
- { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
- { ANY KIND, either express or implied. See the License for the specific language governing rights }
- { and limitations under the License. }
- { }
- { The Original Code is JclSynch.pas. }
- { }
- { The Initial Developers of the Original Code are Marcel van Brakel and Azret Botash. }
- { Portions created by these individuals are Copyright (C) of these individuals. }
- { All Rights Reserved. }
- { }
- { Contributor(s): }
- { Marcel van Brakel }
- { Olivier Sannier (obones) }
- { Matthias Thoma (mthoma) }
- { }
- {**************************************************************************************************}
- { }
- { This unit contains various classes and support routines for implementing synchronisation in }
- { multithreaded applications. This ranges from interlocked access to simple typed variables to }
- { wrapper classes for synchronisation primitives provided by the operating system }
- { (critical section, semaphore, mutex etc). It also includes three user defined classes to }
- { complement these. }
- { }
- {**************************************************************************************************}
- { }
- { Last modified: $Date:: $ }
- { Revision: $Rev:: $ }
- { Author: $Author:: $ }
- { }
- {**************************************************************************************************}
- unit JclSynch;
- {$I jcl.inc}
- interface
- uses
- {$IFDEF UNITVERSIONING}
- JclUnitVersioning,
- {$ENDIF UNITVERSIONING}
- {$IFDEF HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Winapi.Windows, JclWin32,
- {$ENDIF MSWINDOWS}
- {$ELSE ~HAS_UNITSCOPE}
- {$IFDEF MSWINDOWS}
- Windows, JclWin32,
- {$ENDIF MSWINDOWS}
- {$ENDIF ~HAS_UNITSCOPE}
- JclBase;
- // Locked Integer manipulation
- //
- // Routines to manipulate simple typed variables in a thread safe manner
- function LockedAdd(var Target: Integer; Value: Integer): Integer; overload;
- function LockedCompareExchange(var Target: Integer; Exch, Comp: Integer): Integer; overload;
- function LockedCompareExchange(var Target: TObject; Exch, Comp: TObject): TObject; overload;
- function LockedCompareExchange(var Target: Pointer; Exch, Comp: Pointer): Pointer; overload;
- function LockedDec(var Target: Integer): Integer; overload;
- function LockedExchange(var Target: Integer; Value: Integer): Integer; overload;
- function LockedExchangeAdd(var Target: Integer; Value: Integer): Integer; overload;
- function LockedExchangeDec(var Target: Integer): Integer; overload;
- function LockedExchangeInc(var Target: Integer): Integer; overload;
- function LockedExchangeSub(var Target: Integer; Value: Integer): Integer; overload;
- function LockedInc(var Target: Integer): Integer; overload;
- function LockedSub(var Target: Integer; Value: Integer): Integer; overload;
- {$IFDEF CPU64}
- function LockedAdd(var Target: Int64; Value: Int64): Int64; overload;
- function LockedCompareExchange(var Target: Int64; Exch, Comp: Int64): Int64; overload;
- function LockedDec(var Target: Int64): Int64; overload;
- function LockedExchange(var Target: Int64; Value: Int64): Int64; overload;
- function LockedExchangeAdd(var Target: Int64; Value: Int64): Int64; overload;
- function LockedExchangeDec(var Target: Int64): Int64; overload;
- function LockedExchangeInc(var Target: Int64): Int64; overload;
- function LockedExchangeSub(var Target: Int64; Value: Int64): Int64; overload;
- function LockedInc(var Target: Int64): Int64; overload;
- function LockedSub(var Target: Int64; Value: Int64): Int64; overload;
- {$IFDEF BORLAND}
- {$IFNDEF COMPILER29_UP}
- function LockedDec(var Target: NativeInt): NativeInt; overload;
- function LockedInc(var Target: NativeInt): NativeInt; overload;
- {$ENDIF ~COMPILER29_UP}
- {$ENDIF BORLAND}
- {$ENDIF CPU64}
- // TJclDispatcherObject
- //
- // Base class for operating system provided synchronisation primitives
- type
- TJclWaitResult = (wrAbandoned, wrError, wrIoCompletion, wrSignaled, wrTimeout);
- TJclWaitHandle = THandle;
- TJclDispatcherObject = class(TObject)
- private
- FExisted: Boolean;
- FHandle: TJclWaitHandle;
- FName: string;
- public
- constructor Attach(AHandle: TJclWaitHandle);
- destructor Destroy; override;
- //function MsgWaitFor(const TimeOut: Cardinal): TJclWaitResult; Mask: DWORD): TJclWaitResult;
- //function MsgWaitForEx(const TimeOut: Cardinal): TJclWaitResult; Mask: DWORD): TJclWaitResult;
- function SignalAndWait(const Obj: TJclDispatcherObject; TimeOut: Cardinal;
- Alertable: Boolean): TJclWaitResult;
- function WaitAlertable(const TimeOut: Cardinal): TJclWaitResult;
- function WaitFor(const TimeOut: Cardinal): TJclWaitResult;
- function WaitForever: TJclWaitResult;
- property Existed: Boolean read FExisted;
- property Handle: TJclWaitHandle read FHandle;
- property Name: string read FName;
- end;
- // Wait functions
- //
- // Object enabled Wait functions (takes TJclDispatcher objects as parameter as
- // opposed to handles) mostly for convenience
- function WaitForMultipleObjects(const Objects: array of TJclDispatcherObject;
- WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
- function WaitAlertableForMultipleObjects(const Objects: array of TJclDispatcherObject;
- WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
- type
- TJclCriticalSection = class(TObject)
- private
- FCriticalSection: TRTLCriticalSection;
- public
- constructor Create; virtual;
- destructor Destroy; override;
- class procedure CreateAndEnter(var CS: TJclCriticalSection);
- procedure Enter;
- procedure Leave;
- end;
- TJclCriticalSectionEx = class(TJclCriticalSection)
- private
- FSpinCount: Cardinal;
- function GetSpinCount: Cardinal;
- procedure SetSpinCount(const Value: Cardinal);
- public
- constructor Create; override;
- constructor CreateEx(SpinCount: Cardinal; NoFailEnter: Boolean); virtual;
- class function GetSpinTimeOut: Cardinal;
- class procedure SetSpinTimeOut(const Value: Cardinal);
- function TryEnter: Boolean;
- property SpinCount: Cardinal read GetSpinCount write SetSpinCount;
- end;
- TJclEvent = class(TJclDispatcherObject)
- public
- constructor Create(SecAttr: PSecurityAttributes; Manual, Signaled: Boolean; const Name: string);
- constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
- function Pulse: Boolean;
- function ResetEvent: Boolean;
- function SetEvent: Boolean;
- end;
- TJclWaitableTimer = class(TJclDispatcherObject)
- private
- FResume: Boolean;
- public
- constructor Create(SecAttr: PSecurityAttributes; Manual: Boolean; const Name: string);
- constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
- function Cancel: Boolean;
- function SetTimer(const DueTime: Int64; Period: Longint; Resume: Boolean): Boolean;
- function SetTimerApc(const DueTime: Int64; Period: Longint; Resume: Boolean; Apc: TFNTimerAPCRoutine; Arg: Pointer): Boolean;
- end;
- TJclSemaphore = class(TJclDispatcherObject)
- public
- constructor Create(SecAttr: PSecurityAttributes; Initial, Maximum: Longint; const Name: string);
- constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
- function Release(ReleaseCount: Longint): Boolean;
- function ReleasePrev(ReleaseCount: Longint; var PrevCount: Longint): Boolean;
- end;
- TJclMutex = class(TJclDispatcherObject)
- public
- constructor Create(SecAttr: PSecurityAttributes; InitialOwner: Boolean;
- const Name: string);
- constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
- function Acquire(const TimeOut: Cardinal = INFINITE): Boolean;
- function Release: Boolean;
- end;
- POptexSharedInfo = ^TOptexSharedInfo;
- TOptexSharedInfo = record
- SpinCount: Integer; // number of times to try and enter the optex before
- // waiting on kernel event, 0 on single processor
- LockCount: Integer; // count of enter attempts
- ThreadId: Longword; // id of thread that owns the optex, 0 if free
- RecursionCount: Integer; // number of times the optex is owned, 0 if free
- end;
- TJclOptex = class(TObject)
- private
- FEvent: TJclEvent;
- FExisted: Boolean;
- FFileMapping: THandle;
- FName: string;
- FSharedInfo: POptexSharedInfo;
- function GetUniProcess: Boolean;
- function GetSpinCount: Integer;
- procedure SetSpinCount(Value: Integer);
- public
- constructor Create(const Name: string = ''; SpinCount: Integer = 4000);
- destructor Destroy; override;
- procedure Enter;
- procedure Leave;
- function TryEnter: Boolean;
- property Existed: Boolean read FExisted;
- property Name: string read FName;
- property SpinCount: Integer read GetSpinCount write SetSpinCount;
- property UniProcess: Boolean read GetUniProcess;
- end;
- TMrewPreferred = (mpReaders, mpWriters, mpEqual);
- TMrewThreadInfo = record
- ThreadId: Longword; // client-id of thread
- RecursionCount: Integer; // number of times a thread accessed the mrew
- Reader: Boolean; // true if reader, false if writer
- end;
- TMrewThreadInfoArray = array of TMrewThreadInfo;
- TJclMultiReadExclusiveWrite = class(TObject)
- private
- FLock: TJclCriticalSection;
- FPreferred: TMrewPreferred;
- FSemReaders: TJclSemaphore;
- FSemWriters: TJclSemaphore;
- FState: Integer;
- FThreads: TMrewThreadInfoArray;
- FWaitingReaders: Integer;
- FWaitingWriters: Integer;
- procedure AddToThreadList(ThreadId: Longword; Reader: Boolean);
- procedure RemoveFromThreadList(Index: Integer);
- function FindThread(ThreadId: Longword): Integer;
- procedure ReleaseWaiters(WasReading: Boolean);
- protected
- procedure Release;
- public
- constructor Create(Preferred: TMrewPreferred);
- destructor Destroy; override;
- procedure BeginRead;
- procedure BeginWrite;
- procedure EndRead;
- procedure EndWrite;
- end;
- PMetSectSharedInfo = ^TMetSectSharedInfo;
- TMetSectSharedInfo = record
- Initialized: LongBool; // Is the metered section initialized?
- SpinLock: Longint; // Used to gain access to this structure
- ThreadsWaiting: Longint; // Count of threads waiting
- AvailableCount: Longint; // Available resource count
- MaximumCount: Longint; // Maximum resource count
- end;
- PMeteredSection = ^TMeteredSection;
- TMeteredSection = record
- Event: THandle; // Handle to a kernel event object
- FileMap: THandle; // Handle to memory mapped file
- SharedInfo: PMetSectSharedInfo;
- end;
- TJclMeteredSection = class(TObject)
- private
- FMetSect: PMeteredSection;
- procedure CloseMeteredSection;
- function InitMeteredSection(InitialCount, MaxCount: Longint; const Name: string; OpenOnly: Boolean): Boolean;
- function CreateMetSectEvent(const Name: string; OpenOnly: Boolean): Boolean;
- function CreateMetSectFileView(InitialCount, MaxCount: Longint; const Name: string; OpenOnly: Boolean): Boolean;
- protected
- procedure AcquireLock;
- procedure ReleaseLock;
- public
- constructor Create(InitialCount, MaxCount: Longint; const Name: string);
- constructor Open(const Name: string);
- destructor Destroy; override;
- function Enter(TimeOut: Longword): TJclWaitResult;
- function Leave(ReleaseCount: Longint): Boolean; overload;
- function Leave(ReleaseCount: Longint; out PrevCount: Longint): Boolean; overload;
- end;
- // Debugging
- //
- // Note that the following function and structure declarations are all offically
- // undocumented and, except for QueryCriticalSection, require Windows NT since
- // it is all part of the Windows NT Native API.
- { TODO -cTest : Test this structures }
- type
- TEventInfo = record
- EventType: Longint; // 0 = manual, otherwise auto
- Signaled: LongBool; // true is signaled
- end;
- TMutexInfo = record
- SignalState: Longint; // >0 = signaled, <0 = |SignalState| recurs. acquired
- Owned: ByteBool; // owned by thread
- Abandoned: ByteBool; // is abandoned?
- end;
- TSemaphoreCounts = record
- CurrentCount: Longint; // current semaphore count
- MaximumCount: Longint; // maximum semaphore count
- end;
- TTimerInfo = record
- Remaining: TLargeInteger; // 100ns intervals until signaled
- Signaled: ByteBool; // is signaled?
- end;
- function QueryCriticalSection(CS: TJclCriticalSection; var Info: TRTLCriticalSection): Boolean;
- { TODO -cTest : Test these 4 functions }
- function QueryEvent(Handle: THandle; var Info: TEventInfo): Boolean;
- function QueryMutex(Handle: THandle; var Info: TMutexInfo): Boolean;
- function QuerySemaphore(Handle: THandle; var Info: TSemaphoreCounts): Boolean;
- function QueryTimer(Handle: THandle; var Info: TTimerInfo): Boolean;
- type
- // Exceptions
- EJclWin32HandleObjectError = class(EJclWin32Error);
- EJclDispatcherObjectError = class(EJclWin32Error);
- EJclCriticalSectionError = class(EJclWin32Error);
- EJclEventError = class(EJclWin32Error);
- EJclWaitableTimerError = class(EJclWin32Error);
- EJclSemaphoreError = class(EJclWin32Error);
- EJclMutexError = class(EJclWin32Error);
- EJclMeteredSectionError = class(EJclError);
- function ValidateMutexName(const aName: string): string;
- {$IFDEF UNITVERSIONING}
- const
- UnitVersioning: TUnitVersionInfo = (
- RCSfile: '$URL$';
- Revision: '$Revision$';
- Date: '$Date$';
- LogPath: 'JCL\source\common';
- Extra: '';
- Data: nil
- );
- {$ENDIF UNITVERSIONING}
- implementation
- uses
- {$IFDEF HAS_UNITSCOPE}
- System.SysUtils,
- {$ELSE ~HAS_UNITSCOPE}
- SysUtils,
- {$ENDIF ~HAS_UNITSCOPE}
- JclLogic, JclRegistry, JclResources,
- JclSysInfo, JclStrings;
- const
- RegSessionManager = {HKLM\} 'SYSTEM\CurrentControlSet\Control\Session Manager';
- RegCritSecTimeout = {RegSessionManager\} 'CriticalSectionTimeout';
- // Locked Integer manipulation
- function LockedAdd(var Target: Integer; Value: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Value
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, EDX
- LOCK XADD [ECX], EAX
- ADD EAX, EDX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Value
- // <-- EAX Result
- MOV EAX, EDX
- LOCK XADD [RCX], EAX
- ADD EAX, EDX
- {$ENDIF CPU64}
- end;
- function LockedCompareExchange(var Target: Integer; Exch, Comp: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Exch
- // ECX Comp
- // <-- EAX Result
- XCHG EAX, ECX
- // EAX Comp
- // EDX Exch
- // ECX Target
- LOCK CMPXCHG [ECX], EDX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Exch
- // R8 Comp
- // <-- EAX Result
- MOV RAX, R8
- // RCX Target
- // EDX Exch
- // RAX Comp
- LOCK CMPXCHG [RCX], EDX
- {$ENDIF CPU64}
- end;
- function LockedCompareExchange(var Target: Pointer; Exch, Comp: Pointer): Pointer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Exch
- // ECX Comp
- // <-- EAX Result
- XCHG EAX, ECX
- // EAX Comp
- // EDX Exch
- // ECX Target
- LOCK CMPXCHG [ECX], EDX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // RDX Exch
- // R8 Comp
- // <-- RAX Result
- MOV RAX, R8
- // RCX Target
- // RDX Exch
- // RAX Comp
- LOCK CMPXCHG [RCX], RDX
- {$ENDIF CPU64}
- end;
- function LockedCompareExchange(var Target: TObject; Exch, Comp: TObject): TObject;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Exch
- // ECX Comp
- // <-- EAX Result
- XCHG EAX, ECX
- // EAX Comp
- // EDX Exch
- // ECX Target
- LOCK CMPXCHG [ECX], EDX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // RDX Exch
- // R8 Comp
- // <-- RAX Result
- MOV RAX, R8
- // --> RCX Target
- // RDX Exch
- // RAX Comp
- LOCK CMPXCHG [RCX], RDX
- {$ENDIF CPU64}
- end;
- function LockedDec(var Target: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, -1
- LOCK XADD [ECX], EAX
- DEC EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // <-- EAX Result
- MOV EAX, -1
- LOCK XADD [RCX], EAX
- DEC EAX
- {$ENDIF CPU64}
- end;
- function LockedExchange(var Target: Integer; Value: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Value
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, EDX
- // ECX Target
- // EAX Value
- LOCK XCHG [ECX], EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Value
- // <-- EAX Result
- MOV EAX, EDX
- // RCX Target
- // EAX Value
- LOCK XCHG [RCX], EAX
- {$ENDIF CPU64}
- end;
- function LockedExchangeAdd(var Target: Integer; Value: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Value
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, EDX
- // ECX Target
- // EAX Value
- LOCK XADD [ECX], EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Value
- // <-- EAX Result
- MOV EAX, EDX
- // RCX Target
- // EAX Value
- LOCK XADD [RCX], EAX
- {$ENDIF CPU64}
- end;
- function LockedExchangeDec(var Target: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, -1
- LOCK XADD [ECX], EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // <-- EAX Result
- MOV EAX, -1
- LOCK XADD [RCX], EAX
- {$ENDIF CPU64}
- end;
- function LockedExchangeInc(var Target: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, 1
- LOCK XADD [ECX], EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // <-- EAX Result
- MOV EAX, 1
- LOCK XADD [RCX], EAX
- {$ENDIF CPU64}
- end;
- function LockedExchangeSub(var Target: Integer; Value: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Value
- // <-- EAX Result
- MOV ECX, EAX
- NEG EDX
- MOV EAX, EDX
- // ECX Target
- // EAX -Value
- LOCK XADD [ECX], EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Value
- // <-- EAX Result
- NEG EDX
- MOV EAX, EDX
- // RCX Target
- // EAX -Value
- LOCK XADD [RCX], EAX
- {$ENDIF CPU64}
- end;
- function LockedInc(var Target: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // <-- EAX Result
- MOV ECX, EAX
- MOV EAX, 1
- LOCK XADD [ECX], EAX
- INC EAX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // <-- EAX Result
- MOV EAX, 1
- LOCK XADD [RCX], EAX
- INC EAX
- {$ENDIF CPU64}
- end;
- function LockedSub(var Target: Integer; Value: Integer): Integer;
- asm
- {$IFDEF CPU32}
- // --> EAX Target
- // EDX Value
- // <-- EAX Result
- MOV ECX, EAX
- NEG EDX
- MOV EAX, EDX
- LOCK XADD [ECX], EAX
- ADD EAX, EDX
- {$ENDIF CPU32}
- {$IFDEF CPU64}
- // --> RCX Target
- // EDX Value
- // <-- EAX Result
- NEG EDX
- MOV EAX, EDX
- LOCK XADD [RCX], EAX
- ADD EAX, EDX
- {$ENDIF CPU64}
- end;
- {$IFDEF CPU64}
- // Locked Int64 manipulation
- function LockedAdd(var Target: Int64; Value: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Value
- // <-- RAX Result
- MOV RAX, RDX
- LOCK XADD [RCX], RAX
- ADD RAX, RDX
- end;
- function LockedCompareExchange(var Target: Int64; Exch, Comp: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Exch
- // R8 Comp
- // <-- RAX Result
- MOV RAX, R8
- LOCK CMPXCHG [RCX], RDX
- end;
- function LockedDec(var Target: Int64): Int64;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, -1
- LOCK XADD [RCX], RAX
- DEC RAX
- end;
- function LockedExchange(var Target: Int64; Value: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Value
- // <-- RAX Result
- MOV RAX, RDX
- LOCK XCHG [RCX], RAX
- end;
- function LockedExchangeAdd(var Target: Int64; Value: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Value
- // <-- RAX Result
- MOV RAX, RDX
- LOCK XADD [RCX], RAX
- end;
- function LockedExchangeDec(var Target: Int64): Int64;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, -1
- LOCK XADD [RCX], RAX
- end;
- function LockedExchangeInc(var Target: Int64): Int64;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, 1
- LOCK XADD [RCX], RAX
- end;
- function LockedExchangeSub(var Target: Int64; Value: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Value
- // <-- RAX Result
- NEG RDX
- MOV RAX, RDX
- LOCK XADD [RCX], RAX
- end;
- function LockedInc(var Target: Int64): Int64;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, 1
- LOCK XADD [RCX], RAX
- INC RAX
- end;
- function LockedSub(var Target: Int64; Value: Int64): Int64;
- asm
- // --> RCX Target
- // RDX Value
- // <-- RAX Result
- NEG RDX
- MOV RAX, RDX
- LOCK XADD [RCX], RAX
- ADD RAX, RDX
- end;
- {$IFDEF BORLAND}
- {$IFNDEF COMPILER29_UP}
- function LockedDec(var Target: NativeInt): NativeInt;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, -1
- LOCK XADD [RCX], RAX
- DEC RAX
- end;
- function LockedInc(var Target: NativeInt): NativeInt;
- asm
- // --> RCX Target
- // <-- RAX Result
- MOV RAX, 1
- LOCK XADD [RCX], RAX
- INC RAX
- end;
- {$ENDIF ~COMPILER29_UP}
- {$ENDIF BORLAND}
- {$ENDIF CPU64}
- //=== { TJclDispatcherObject } ===============================================
- function MapSignalResult(const Ret: DWORD): TJclWaitResult;
- begin
- case Ret of
- WAIT_ABANDONED:
- Result := wrAbandoned;
- WAIT_OBJECT_0:
- Result := wrSignaled;
- WAIT_TIMEOUT:
- Result := wrTimeout;
- WAIT_IO_COMPLETION:
- Result := wrIoCompletion;
- WAIT_FAILED:
- Result := wrError;
- else
- Result := wrError;
- end;
- end;
- constructor TJclDispatcherObject.Attach(AHandle: TJclWaitHandle);
- begin
- inherited Create;
- FExisted := True;
- FHandle := AHandle;
- FName := '';
- end;
- destructor TJclDispatcherObject.Destroy;
- begin
- CloseHandle(FHandle);
- inherited Destroy;
- end;
- { TODO: Use RTDL Version of SignalObjectAndWait }
- function TJclDispatcherObject.SignalAndWait(const Obj: TJclDispatcherObject;
- TimeOut: Cardinal; Alertable: Boolean): TJclWaitResult;
- begin
- // Note: Do not make this method virtual! It's only available on NT 4 up...
- Result := MapSignalResult(Cardinal({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SignalObjectAndWait(Obj.Handle, Handle, TimeOut, Alertable)));
- end;
- function TJclDispatcherObject.WaitAlertable(const TimeOut: Cardinal): TJclWaitResult;
- begin
- Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObjectEx(FHandle, TimeOut, True));
- end;
- function TJclDispatcherObject.WaitFor(const TimeOut: Cardinal): TJclWaitResult;
- begin
- Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObject(FHandle, TimeOut));
- end;
- function TJclDispatcherObject.WaitForever: TJclWaitResult;
- begin
- Result := WaitFor(INFINITE);
- end;
- // Wait functions
- function WaitForMultipleObjects(const Objects: array of TJclDispatcherObject;
- WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
- var
- Handles: array of TJclWaitHandle;
- I, Count: Integer;
- begin
- Count := High(Objects) + 1;
- SetLength(Handles, Count);
- for I := 0 to Count - 1 do
- Handles[I] := Objects[I].Handle;
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForMultipleObjects(Count, @Handles[0], WaitAll, TimeOut);
- end;
- function WaitAlertableForMultipleObjects(const Objects: array of TJclDispatcherObject;
- WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
- var
- Handles: array of TJclWaitHandle;
- I, Count: Integer;
- begin
- Count := High(Objects) + 1;
- SetLength(Handles, Count);
- for I := 0 to Count - 1 do
- Handles[I] := Objects[I].Handle;
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForMultipleObjectsEx(Count, @Handles[0], WaitAll, TimeOut, True);
- end;
- //=== { TJclCriticalSection } ================================================
- constructor TJclCriticalSection.Create;
- begin
- inherited Create;
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InitializeCriticalSection(FCriticalSection);
- end;
- destructor TJclCriticalSection.Destroy;
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.DeleteCriticalSection(FCriticalSection);
- inherited Destroy;
- end;
- class procedure TJclCriticalSection.CreateAndEnter(var CS: TJclCriticalSection);
- var
- NewCritSect: TJclCriticalSection;
- begin
- NewCritSect := TJclCriticalSection.Create;
- if LockedCompareExchange(Pointer(CS), Pointer(NewCritSect), nil) <> nil then
- begin
- // LoadInProgress was <> nil -> no exchange took place, free the CS
- NewCritSect.Free;
- end;
- CS.Enter;
- end;
- procedure TJclCriticalSection.Enter;
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.EnterCriticalSection(FCriticalSection);
- end;
- procedure TJclCriticalSection.Leave;
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.LeaveCriticalSection(FCriticalSection);
- end;
- //== { TJclCriticalSectionEx } ===============================================
- const
- DefaultCritSectSpinCount = 4000;
- constructor TJclCriticalSectionEx.Create;
- begin
- CreateEx(DefaultCritSectSpinCount, False);
- end;
- { TODO: Use RTDL Version of InitializeCriticalSectionAndSpinCount }
- constructor TJclCriticalSectionEx.CreateEx(SpinCount: Cardinal;
- NoFailEnter: Boolean);
- begin
- FSpinCount := SpinCount;
- if NoFailEnter then
- SpinCount := SpinCount or Cardinal($80000000);
- if not InitializeCriticalSectionAndSpinCount(FCriticalSection, SpinCount) then
- raise EJclCriticalSectionError.CreateRes(@RsSynchInitCriticalSection);
- end;
- function TJclCriticalSectionEx.GetSpinCount: Cardinal;
- begin
- // Spinning only makes sense on multiprocessor systems. On a single processor
- // system the thread would simply waste cycles while the owning thread is
- // suspended and thus cannot release the critical section.
- if ProcessorCount = 1 then
- Result := 0
- else
- Result := FSpinCount;
- end;
- class function TJclCriticalSectionEx.GetSpinTimeOut: Cardinal;
- begin
- Result := Cardinal(RegReadInteger(HKEY_LOCAL_MACHINE, RegSessionManager,
- RegCritSecTimeout));
- end;
- { TODO: Use RTLD version of SetCriticalSectionSpinCount }
- procedure TJclCriticalSectionEx.SetSpinCount(const Value: Cardinal);
- begin
- FSpinCount := SetCriticalSectionSpinCount(FCriticalSection, Value);
- end;
- class procedure TJclCriticalSectionEx.SetSpinTimeOut(const Value: Cardinal);
- begin
- RegWriteInteger(HKEY_LOCAL_MACHINE, RegSessionManager, RegCritSecTimeout,
- Integer(Value));
- end;
- { TODO: Use RTLD version of TryEnterCriticalSection }
- function TJclCriticalSectionEx.TryEnter: Boolean;
- begin
- Result := TryEnterCriticalSection(FCriticalSection);
- end;
- //== { TJclEvent } ===========================================================
- constructor TJclEvent.Create(SecAttr: PSecurityAttributes; Manual, Signaled: Boolean; const Name: string);
- begin
- inherited Create;
- FName := Name;
- FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(SecAttr, Manual, Signaled, PChar(FName));
- if FHandle = 0 then
- raise EJclEventError.CreateRes(@RsSynchCreateEvent);
- FExisted := GetLastError = ERROR_ALREADY_EXISTS;
- end;
- constructor TJclEvent.Open(Access: Cardinal; Inheritable: Boolean;
- const Name: string);
- begin
- FName := Name;
- FExisted := True;
- FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenEvent(Access, Inheritable, PChar(Name));
- if FHandle = 0 then
- raise EJclEventError.CreateRes(@RsSynchOpenEvent);
- end;
- function TJclEvent.Pulse: Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.PulseEvent(FHandle);
- end;
- function TJclEvent.ResetEvent: Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ResetEvent(FHandle);
- end;
- function TJclEvent.SetEvent: Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetEvent(FHandle);
- end;
- //=== { TJclWaitableTimer } ==================================================
- { TODO: Use RTLD version of CreateWaitableTimer }
- constructor TJclWaitableTimer.Create(SecAttr: PSecurityAttributes;
- Manual: Boolean; const Name: string);
- begin
- FName := Name;
- FResume := False;
- FHandle := CreateWaitableTimer(SecAttr, Manual, PChar(Name));
- if FHandle = 0 then
- raise EJclWaitableTimerError.CreateRes(@RsSynchCreateWaitableTimer);
- FExisted := GetLastError = ERROR_ALREADY_EXISTS;
- end;
- { TODO: Use RTLD version of CancelWaitableTimer }
- function TJclWaitableTimer.Cancel: Boolean;
- begin
- Result := CancelWaitableTimer(FHandle);
- end;
- { TODO: Use RTLD version of OpenWaitableTimer }
- constructor TJclWaitableTimer.Open(Access: Cardinal; Inheritable: Boolean;
- const Name: string);
- begin
- FExisted := True;
- FName := Name;
- FResume := False;
- FHandle := OpenWaitableTimer(Access, Inheritable, PChar(Name));
- if FHandle = 0 then
- raise EJclWaitableTimerError.CreateRes(@RsSynchOpenWaitableTimer);
- end;
- { TODO: Use RTLD version of SetWaitableTimer }
- function TJclWaitableTimer.SetTimer(const DueTime: Int64; Period: Longint;
- Resume: Boolean): Boolean;
- var
- DT: Int64;
- begin
- DT := DueTime;
- FResume := Resume;
- Result := SetWaitableTimer(FHandle, DT, Period, nil, nil, FResume);
- end;
- { TODO -cHelp : OS restrictions }
- function TJclWaitableTimer.SetTimerApc(const DueTime: Int64; Period: Longint;
- Resume: Boolean; Apc: TFNTimerAPCRoutine; Arg: Pointer): Boolean;
- var
- DT: Int64;
- begin
- DT := DueTime;
- FResume := Resume;
- Result := RtdlSetWaitableTimer(FHandle, DT, Period, Apc, Arg, FResume);
- { TODO : Exception for Win9x, older WinNT? }
- // if not Result and (GetLastError = ERROR_CALL_NOT_IMPLEMENTED) then
- // RaiseLastOSError;
- end;
- //== { TJclSemaphore } =======================================================
- constructor TJclSemaphore.Create(SecAttr: PSecurityAttributes;
- Initial, Maximum: Integer; const Name: string);
- begin
- Assert((Initial >= 0) and (Maximum > 0));
- FName := Name;
- FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateSemaphore(SecAttr, Initial, Maximum, PChar(Name));
- if FHandle = 0 then
- raise EJclSemaphoreError.CreateRes(@RsSynchCreateSemaphore);
- FExisted := GetLastError = ERROR_ALREADY_EXISTS;
- end;
- constructor TJclSemaphore.Open(Access: Cardinal; Inheritable: Boolean;
- const Name: string);
- begin
- FName := Name;
- FExisted := True;
- FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenSemaphore(Access, Inheritable, PChar(Name));
- if FHandle = 0 then
- raise EJclSemaphoreError.CreateRes(@RsSynchOpenSemaphore);
- end;
- function TJclSemaphore.ReleasePrev(ReleaseCount: Longint;
- var PrevCount: Longint): Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseSemaphore(FHandle, ReleaseCount, @PrevCount);
- end;
- function TJclSemaphore.Release(ReleaseCount: Integer): Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseSemaphore(FHandle, ReleaseCount, nil);
- end;
- //=== { TJclMutex } ==========================================================
- function TJclMutex.Acquire(const TimeOut: Cardinal): Boolean;
- begin
- Result := WaitFor(TimeOut) = wrSignaled;
- end;
- constructor TJclMutex.Create(SecAttr: PSecurityAttributes; InitialOwner: Boolean; const Name: string);
- begin
- inherited Create;
- FName := Name;
- FHandle := JclWin32.CreateMutex(SecAttr, InitialOwner, PChar(Name));
- if FHandle = 0 then
- raise EJclMutexError.CreateRes(@RsSynchCreateMutex);
- FExisted := GetLastError = ERROR_ALREADY_EXISTS;
- end;
- constructor TJclMutex.Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
- begin
- inherited Create;
- FName := Name;
- FExisted := True;
- FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenMutex(Access, Inheritable, PChar(Name));
- if FHandle = 0 then
- raise EJclMutexError.CreateRes(@RsSynchOpenMutex);
- end;
- function TJclMutex.Release: Boolean;
- begin
- Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseMutex(FHandle);
- end;
- //=== { TJclOptex } ==========================================================
- constructor TJclOptex.Create(const Name: string; SpinCount: Integer);
- begin
- FExisted := False;
- FName := Name;
- if Name = '' then
- begin
- // None shared optex, don't need filemapping, sharedinfo is local
- FFileMapping := 0;
- FEvent := TJclEvent.Create(nil, False, False, '');
- FSharedInfo := AllocMem(SizeOf(TOptexSharedInfo));
- end
- else
- begin
- // Shared optex, event protects access to sharedinfo. Creation of filemapping
- // doesn't need protection as it will automatically "open" instead of "create"
- // if another process already created it.
- FEvent := TJclEvent.Create(nil, False, False, 'Optex_Event_' + Name);
- FExisted := FEvent.Existed;
- FFileMapping := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
- 0, SizeOf(TOptexSharedInfo), PChar('Optex_MMF_' + Name));
- Assert(FFileMapping <> 0);
- FSharedInfo := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.MapViewOfFile(FFileMapping, FILE_MAP_WRITE, 0, 0, 0);
- Assert(FSharedInfo <> nil);
- end;
- SetSpinCount(SpinCount);
- end;
- destructor TJclOptex.Destroy;
- begin
- FreeAndNil(FEvent);
- if UniProcess then
- FreeMem(FSharedInfo)
- else
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.UnmapViewOfFile(FSharedInfo);
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FFileMapping);
- end;
- inherited Destroy;
- end;
- procedure TJclOptex.Enter;
- var
- ThreadId: Longword;
- begin
- if TryEnter then
- Exit;
- ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
- if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedIncrement(FSharedInfo^.LockCount) = 1 then
- begin
- // Optex was unowned
- FSharedInfo^.ThreadId := ThreadId;
- FSharedInfo^.RecursionCount := 1;
- end
- else
- begin
- if FSharedInfo^.ThreadId = ThreadId then
- begin
- // We already owned it, increase ownership count
- Inc(FSharedInfo^.RecursionCount)
- end
- else
- begin
- // Optex is owner by someone else, wait for it to be released and then
- // immediately take ownership
- FEvent.WaitForever;
- FSharedInfo^.ThreadId := ThreadId;
- FSharedInfo^.RecursionCount := 1;
- end;
- end;
- end;
- function TJclOptex.GetSpinCount: Integer;
- begin
- Result := FSharedInfo^.SpinCount;
- end;
- function TJclOptex.GetUniProcess: Boolean;
- begin
- Result := FFileMapping = 0;
- end;
- procedure TJclOptex.Leave;
- begin
- Dec(FSharedInfo^.RecursionCount);
- if FSharedInfo^.RecursionCount > 0 then
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedDecrement(FSharedInfo^.LockCount)
- else
- begin
- FSharedInfo^.ThreadId := 0;
- if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedDecrement(FSharedInfo^.LockCount) > 0 then
- FEvent.SetEvent;
- end;
- end;
- procedure TJclOptex.SetSpinCount(Value: Integer);
- begin
- if Value < 0 then
- Value := DefaultCritSectSpinCount;
- // Spinning only makes sense on multiprocessor systems
- if ProcessorCount > 1 then
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(Integer(FSharedInfo^.SpinCount), Value);
- end;
- function TJclOptex.TryEnter: Boolean;
- var
- ThreadId: Longword;
- ThreadOwnsOptex: Boolean;
- SpinCount: Integer;
- begin
- ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
- SpinCount := FSharedInfo^.SpinCount;
- repeat
- //ThreadOwnsOptex := InterlockedCompareExchange(Pointer(FSharedInfo^.LockCount),
- // Pointer(1), Pointer(0)) = Pointer(0); // not available on win95
- ThreadOwnsOptex := LockedCompareExchange(FSharedInfo^.LockCount, 1, 0) = 0;
- if ThreadOwnsOptex then
- begin
- // Optex was unowned
- FSharedInfo^.ThreadId := ThreadId;
- FSharedInfo^.RecursionCount := 1;
- end
- else
- begin
- if FSharedInfo^.ThreadId = ThreadId then
- begin
- // We already owned the Optex
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedIncrement(FSharedInfo^.LockCount);
- Inc(FSharedInfo^.RecursionCount);
- ThreadOwnsOptex := True;
- end;
- end;
- Dec(SpinCount);
- until ThreadOwnsOptex or (SpinCount <= 0);
- Result := ThreadOwnsOptex;
- end;
- //=== { TJclMultiReadExclusiveWrite } ========================================
- constructor TJclMultiReadExclusiveWrite.Create(Preferred: TMrewPreferred);
- begin
- inherited Create;
- FLock := TJclCriticalSection.Create;
- FPreferred := Preferred;
- FSemReaders := TJclSemaphore.Create(nil, 0, MaxInt, '');
- FSemWriters := TJclSemaphore.Create(nil, 0, MaxInt, '');
- SetLength(FThreads, 0);
- FState := 0;
- FWaitingReaders := 0;
- FWaitingWriters := 0;
- end;
- destructor TJclMultiReadExclusiveWrite.Destroy;
- begin
- FreeAndNil(FSemReaders);
- FreeAndNil(FSemWriters);
- FreeAndNil(FLock);
- inherited Destroy;
- end;
- procedure TJclMultiReadExclusiveWrite.AddToThreadList(ThreadId: Longword;
- Reader: Boolean);
- var
- L: Integer;
- begin
- // Caller must own lock
- L := Length(FThreads);
- SetLength(FThreads, L + 1);
- FThreads[L].ThreadId := ThreadId;
- FThreads[L].RecursionCount := 1;
- FThreads[L].Reader := Reader;
- end;
- procedure TJclMultiReadExclusiveWrite.BeginRead;
- var
- ThreadId: Longword;
- Index: Integer;
- MustWait: Boolean;
- begin
- MustWait := False;
- ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
- FLock.Enter;
- try
- Index := FindThread(ThreadId);
- if Index >= 0 then
- begin
- // Thread is on threadslist so it is already reading
- Inc(FThreads[Index].RecursionCount);
- end
- else
- begin
- // Request to read (first time)
- AddToThreadList(ThreadId, True);
- if FState >= 0 then
- begin
- // MREW is unowned or only readers. If there are no waiting writers or
- // readers are preferred then allow thread to continue, otherwise it must
- // wait it's turn
- if (FPreferred = mpReaders) or (FWaitingWriters = 0) then
- Inc(FState)
- else
- begin
- Inc(FWaitingReaders);
- MustWait := True;
- end;
- end
- else
- begin
- // MREW is owner by a writer, must wait
- Inc(FWaitingReaders);
- MustWait := True;
- end;
- end;
- finally
- FLock.Leave;
- end;
- if MustWait then
- FSemReaders.WaitForever;
- end;
- procedure TJclMultiReadExclusiveWrite.BeginWrite;
- var
- ThreadId: Longword;
- Index: Integer;
- MustWait: Boolean;
- begin
- MustWait := False;
- FLock.Enter;
- try
- ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
- Index := FindThread(ThreadId);
- if Index < 0 then
- begin
- // Request to write (first time)
- AddToThreadList(ThreadId, False);
- if FState = 0 then
- begin
- // MREW is unowned so start writing
- FState := -1;
- end
- else
- begin
- // MREW is owner, must wait
- Inc(FWaitingWriters);
- MustWait := True;
- end;
- end
- else
- begin
- if FThreads[Index].Reader then
- begin
- // Request to write while reading
- Inc(FThreads[Index].RecursionCount);
- FThreads[Index].Reader := False;
- Dec(FState);
- if FState = 0 then
- begin
- // MREW is unowned so start writing
- FState := -1;
- end
- else
- begin
- // MREW is owned, must wait
- MustWait := True;
- Inc(FWaitingWriters);
- end;
- end
- else
- // Requesting to write while already writing
- Inc(FThreads[Index].RecursionCount);
- end;
- finally
- FLock.Leave;
- end;
- if MustWait then
- FSemWriters.WaitFor(INFINITE);
- end;
- procedure TJclMultiReadExclusiveWrite.EndRead;
- begin
- Release;
- end;
- procedure TJclMultiReadExclusiveWrite.EndWrite;
- begin
- Release;
- end;
- function TJclMultiReadExclusiveWrite.FindThread(ThreadId: Longword): Integer;
- var
- I: Integer;
- begin
- // Caller must lock
- Result := -1;
- for I := 0 to Length(FThreads) - 1 do
- if FThreads[I].ThreadId = ThreadId then
- begin
- Result := I;
- Exit;
- end;
- end;
- procedure TJclMultiReadExclusiveWrite.Release;
- var
- ThreadId: Longword;
- Index: Integer;
- WasReading: Boolean;
- begin
- ThreadId := GetCurrentThreadId;
- FLock.Enter;
- try
- Index := FindThread(ThreadId);
- if Index >= 0 then
- begin
- Dec(FThreads[Index].RecursionCount);
- if FThreads[Index].RecursionCount = 0 then
- begin
- WasReading := FThreads[Index].Reader;
- if WasReading then
- Dec(FState)
- else
- FState := 0;
- RemoveFromThreadList(Index);
- if FState = 0 then
- ReleaseWaiters(WasReading);
- end;
- end;
- finally
- FLock.Leave;
- end;
- end;
- procedure TJclMultiReadExclusiveWrite.ReleaseWaiters(WasReading: Boolean);
- var
- ToRelease: TMrewPreferred;
- begin
- // Caller must Lock
- ToRelease := mpEqual;
- case FPreferred of
- mpReaders:
- if FWaitingReaders > 0 then
- ToRelease := mpReaders
- else
- if FWaitingWriters > 0 then
- ToRelease := mpWriters;
- mpWriters:
- if FWaitingWriters > 0 then
- ToRelease := mpWriters
- else
- if FWaitingReaders > 0 then
- ToRelease := mpReaders;
- mpEqual:
- if WasReading then
- begin
- if FWaitingWriters > 0 then
- ToRelease := mpWriters
- else
- if FWaitingReaders > 0 then
- ToRelease := mpReaders;
- end
- else
- begin
- if FWaitingReaders > 0 then
- ToRelease := mpReaders
- else
- if FWaitingWriters > 0 then
- ToRelease := mpWriters;
- end;
- end;
- case ToRelease of
- mpReaders:
- begin
- FState := FWaitingReaders;
- FWaitingReaders := 0;
- FSemReaders.Release(FState);
- end;
- mpWriters:
- begin
- FState := -1;
- Dec(FWaitingWriters);
- FSemWriters.Release(1);
- end;
- mpEqual:
- // no waiters
- end;
- end;
- procedure TJclMultiReadExclusiveWrite.RemoveFromThreadList(Index: Integer);
- var
- L: Integer;
- begin
- // Caller must Lock
- L := Length(FThreads);
- if Index < (L - 1) then
- Move(FThreads[Index + 1], FThreads[Index], SizeOf(TMrewThreadInfo) * (L - Index - 1));
- SetLength(FThreads, L - 1);
- end;
- //=== { TJclMeteredSection } =================================================
- const
- MAX_METSECT_NAMELEN = 128;
- constructor TJclMeteredSection.Create(InitialCount, MaxCount: Integer; const Name: string);
- begin
- if (MaxCount < 1) or (InitialCount > MaxCount) or (InitialCount < 0) or
- (Length(Name) > MAX_METSECT_NAMELEN) then
- raise EJclMeteredSectionError.CreateRes(@RsMetSectInvalidParameter);
- FMetSect := PMeteredSection(AllocMem(SizeOf(TMeteredSection)));
- if FMetSect <> nil then
- begin
- if not InitMeteredSection(InitialCount, MaxCount, Name, False) then
- begin
- CloseMeteredSection;
- FMetSect := nil;
- raise EJclMeteredSectionError.CreateRes(@RsMetSectInitialize);
- end;
- end;
- end;
- constructor TJclMeteredSection.Open(const Name: string);
- begin
- FMetSect := nil;
- if Name = '' then
- raise EJclMeteredSectionError.CreateRes(@RsMetSectNameEmpty);
- FMetSect := PMeteredSection(AllocMem(SizeOf(TMeteredSection)));
- Assert(FMetSect <> nil);
- if not InitMeteredSection(0, 0, Name, True) then
- begin
- CloseMeteredSection;
- FMetSect := nil;
- raise EJclMeteredSectionError.CreateRes(@RsMetSectInitialize);
- end;
- end;
- destructor TJclMeteredSection.Destroy;
- begin
- CloseMeteredSection;
- inherited Destroy;
- end;
- procedure TJclMeteredSection.AcquireLock;
- begin
- while {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(FMetSect^.SharedInfo^.SpinLock, 1) <> 0 do
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.Sleep(0);
- end;
- procedure TJclMeteredSection.CloseMeteredSection;
- begin
- if FMetSect <> nil then
- begin
- if FMetSect^.SharedInfo <> nil then
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.UnmapViewOfFile(FMetSect^.SharedInfo);
- if FMetSect^.FileMap <> 0 then
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FMetSect^.FileMap);
- if FMetSect^.Event <> 0 then
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FMetSect^.Event);
- FreeMem(FMetSect);
- end;
- end;
- function TJclMeteredSection.CreateMetSectEvent(const Name: string; OpenOnly: Boolean): Boolean;
- var
- FullName: string;
- begin
- if Name = '' then
- FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(nil, False, False, nil)
- else
- begin
- FullName := 'JCL_MSECT_EVT_' + Name;
- if OpenOnly then
- FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenEvent(0, False, PChar(FullName))
- else
- FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(nil, False, False, PChar(FullName));
- end;
- Result := FMetSect^.Event <> 0;
- end;
- function TJclMeteredSection.CreateMetSectFileView(InitialCount, MaxCount: Longint;
- const Name: string; OpenOnly: Boolean): Boolean;
- var
- FullName: string;
- LastError: DWORD;
- begin
- Result := False;
- if Name = '' then
- FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TMetSectSharedInfo), nil)
- else
- begin
- FullName := 'JCL_MSECT_MMF_' + Name;
- if OpenOnly then
- FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenFileMapping(0, False, PChar(FullName))
- else
- FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TMetSectSharedInfo), PChar(FullName));
- end;
- if FMetSect^.FileMap <> 0 then
- begin
- LastError := GetLastError;
- FMetSect^.SharedInfo := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.MapViewOfFile(FMetSect^.FileMap, FILE_MAP_WRITE, 0, 0, 0);
- if FMetSect^.SharedInfo <> nil then
- begin
- if LastError = ERROR_ALREADY_EXISTS then
- while not FMetSect^.SharedInfo^.Initialized do Sleep(0)
- else
- begin
- FMetSect^.SharedInfo^.SpinLock := 0;
- FMetSect^.SharedInfo^.ThreadsWaiting := 0;
- FMetSect^.SharedInfo^.AvailableCount := InitialCount;
- FMetSect^.SharedInfo^.MaximumCount := MaxCount;
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(Integer(FMetSect^.SharedInfo^.Initialized), 1);
- end;
- Result := True;
- end;
- end;
- end;
- function TJclMeteredSection.Enter(TimeOut: Longword): TJclWaitResult;
- begin
- Result := wrSignaled;
- while Result = wrSignaled do
- begin
- AcquireLock;
- try
- if FMetSect^.SharedInfo^.AvailableCount >= 1 then
- begin
- Dec(FMetSect^.SharedInfo^.AvailableCount);
- Result := MapSignalResult(WAIT_OBJECT_0);
- Exit;
- end;
- Inc(FMetSect^.SharedInfo^.ThreadsWaiting);
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ResetEvent(FMetSect^.Event);
- finally
- ReleaseLock;
- end;
- Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObject(FMetSect^.Event, TimeOut));
- end;
- end;
- function TJclMeteredSection.InitMeteredSection(InitialCount, MaxCount: Longint;
- const Name: string; OpenOnly: Boolean): Boolean;
- begin
- Result := False;
- if CreateMetSectEvent(Name, OpenOnly) then
- Result := CreateMetSectFileView(InitialCount, MaxCount, Name, OpenOnly);
- end;
- function TJclMeteredSection.Leave(ReleaseCount: Integer; out PrevCount: Integer): Boolean;
- var
- Count: Integer;
- begin
- Result := False;
- AcquireLock;
- try
- PrevCount := FMetSect^.SharedInfo^.AvailableCount;
- if (ReleaseCount < 0) or
- (FMetSect^.SharedInfo^.AvailableCount + ReleaseCount > FMetSect^.SharedInfo^.MaximumCount) then
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetLastError(ERROR_INVALID_PARAMETER);
- Exit;
- end;
- Inc(FMetSect^.SharedInfo^.AvailableCount, ReleaseCount);
- ReleaseCount := Min(ReleaseCount, FMetSect^.SharedInfo^.ThreadsWaiting);
- if FMetSect^.SharedInfo^.ThreadsWaiting > 0 then
- begin
- for Count := 0 to ReleaseCount - 1 do
- begin
- Dec(FMetSect^.SharedInfo^.ThreadsWaiting);
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetEvent(FMetSect^.Event);
- end;
- end;
- finally
- ReleaseLock;
- end;
- Result := True;
- end;
- function TJclMeteredSection.Leave(ReleaseCount: Integer): Boolean;
- var
- Previous: Longint;
- begin
- Result := Leave(ReleaseCount, Previous);
- end;
- procedure TJclMeteredSection.ReleaseLock;
- begin
- {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(FMetSect^.SharedInfo^.SpinLock, 0);
- end;
- //=== Debugging ==============================================================
- function QueryCriticalSection(CS: TJclCriticalSection; var Info: TRTLCriticalSection): Boolean;
- begin
- Result := CS <> nil;
- if Result then
- Info := CS.FCriticalSection;
- end;
- // Native API functions
- // http://undocumented.ntinternals.net/
- { TODO: RTLD version }
- type
- TNtQueryProc = function (Handle: THandle; InfoClass: Byte; Info: Pointer;
- Len: Longint; ResLen: PLongint): Longint; stdcall;
- var
- _QueryEvent: TNtQueryProc = nil;
- _QueryMutex: TNtQueryProc = nil;
- _QuerySemaphore: TNtQueryProc = nil;
- _QueryTimer: TNtQueryProc = nil;
- function CallQueryProc(var P: TNtQueryProc; const Name: string; Handle: THandle;
- Info: Pointer; InfoSize: Longint): Boolean;
- var
- NtDll: THandle;
- Status: Longint;
- begin
- Result := False;
- if @P = nil then
- begin
- NtDll := GetModuleHandle(PChar('ntdll.dll'));
- if NtDll <> 0 then
- @P := GetProcAddress(NtDll, PChar(Name));
- end;
- if @P <> nil then
- begin
- Status := P(Handle, 0, Info, InfoSize, nil);
- Result := (Status and $80000000) = 0;
- end;
- end;
- function QueryEvent(Handle: THandle; var Info: TEventInfo): Boolean;
- begin
- Result := CallQueryProc(_QueryEvent, 'NtQueryEvent', Handle, @Info, SizeOf(Info));
- end;
- function QueryMutex(Handle: THandle; var Info: TMutexInfo): Boolean;
- begin
- Result := CallQueryProc(_QueryMutex, 'NtQueryMutex', Handle, @Info, SizeOf(Info));
- end;
- function QuerySemaphore(Handle: THandle; var Info: TSemaphoreCounts): Boolean;
- begin
- Result := CallQueryProc(_QuerySemaphore, 'NtQuerySemaphore', Handle, @Info, SizeOf(Info));
- end;
- function QueryTimer(Handle: THandle; var Info: TTimerInfo): Boolean;
- begin
- Result := CallQueryProc(_QueryTimer, 'NtQueryTimer', Handle, @Info, SizeOf(Info));
- end;
- function ValidateMutexName(const aName: string): string;
- const cMutexMaxName = 200;
- begin
- if Length(aName) > cMutexMaxName then
- Result := Copy (aName, Length(aName)-cMutexMaxName, cMutexMaxName)
- else
- Result := aName;
- Result := StrReplaceChar(Result, '\', '_');
- end;
- {$IFDEF UNITVERSIONING}
- initialization
- RegisterUnitVersion(HInstance, UnitVersioning);
- finalization
- UnregisterUnitVersion(HInstance);
- {$ENDIF UNITVERSIONING}
- end.
|