| 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}interfaceuses  {$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 mannerfunction 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 primitivestype  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 conveniencefunction 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}implementationuses  {$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 manipulationfunction 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 manipulationfunction LockedAdd(var Target: Int64; Value: Int64): Int64;asm        // --> RCX Target        //     RDX Value        // <-- RAX Result        MOV     RAX, RDX        LOCK XADD [RCX], RAX        ADD     RAX, RDXend;function LockedCompareExchange(var Target: Int64; Exch, Comp: Int64): Int64;asm        // --> RCX Target        //     RDX Exch        //     R8  Comp        // <-- RAX Result        MOV     RAX, R8        LOCK CMPXCHG [RCX], RDXend;function LockedDec(var Target: Int64): Int64;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, -1        LOCK XADD [RCX], RAX        DEC     RAXend;function LockedExchange(var Target: Int64; Value: Int64): Int64;asm        // --> RCX Target        //     RDX Value        // <-- RAX Result        MOV     RAX, RDX        LOCK XCHG [RCX], RAXend;function LockedExchangeAdd(var Target: Int64; Value: Int64): Int64;asm        // --> RCX Target        //     RDX Value        // <-- RAX Result        MOV     RAX, RDX        LOCK XADD [RCX], RAXend;function LockedExchangeDec(var Target: Int64): Int64;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, -1        LOCK XADD [RCX], RAXend;function LockedExchangeInc(var Target: Int64): Int64;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, 1        LOCK XADD [RCX], RAXend;function LockedExchangeSub(var Target: Int64; Value: Int64): Int64;asm        // --> RCX Target        //     RDX Value        // <-- RAX Result        NEG     RDX        MOV     RAX, RDX        LOCK XADD [RCX], RAXend;function LockedInc(var Target: Int64): Int64;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, 1        LOCK XADD [RCX], RAX        INC     RAXend;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, RDXend;{$IFDEF BORLAND}{$IFNDEF COMPILER29_UP}function LockedDec(var Target: NativeInt): NativeInt;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, -1        LOCK XADD [RCX], RAX        DEC     RAXend;function LockedInc(var Target: NativeInt): NativeInt;asm        // --> RCX Target        // <-- RAX Result        MOV     RAX, 1        LOCK XADD [RCX], RAX        INC     RAXend;{$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 functionsfunction 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.
 |