123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794 |
- {**************************************************************************************************}
- { }
- { 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;
- {$IFNDEF WINSCP}
- function GetSpinCount: Cardinal;
- procedure SetSpinCount(const Value: Cardinal);
- {$ENDIF ~WINSCP}
- public
- constructor Create; override;
- constructor CreateEx(SpinCount: Cardinal; NoFailEnter: Boolean); virtual;
- {$IFNDEF WINSCP}
- class function GetSpinTimeOut: Cardinal;
- class procedure SetSpinTimeOut(const Value: Cardinal);
- {$ENDIF ~WINSCP}
- function TryEnter: Boolean;
- {$IFNDEF WINSCP}
- property SpinCount: Cardinal read GetSpinCount write SetSpinCount;
- {$ENDIF ~WINSCP}
- 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;
- {$IFNDEF WINSCP}
- 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;
- {$ENDIF ~WINSCP}
- 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}
- {$IFNDEF WINSCP}JclLogic, JclRegistry,{$ELSE}Math,{$ENDIF ~WINSCP} JclResources,
- {$IFNDEF WINSCP}JclSysInfo,{$ENDIF ~WINSCP} 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;
- {$IFNDEF WINSCP}
- 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;
- {$ENDIF ~WINSCP}
- { 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;
- {$IFNDEF WINSCP}
- //=== { 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;
- {$ENDIF ~WINSCP}
- //=== { 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.
|