JclSynch.pas 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclSynch.pas. }
  14. { }
  15. { The Initial Developers of the Original Code are Marcel van Brakel and Azret Botash. }
  16. { Portions created by these individuals are Copyright (C) of these individuals. }
  17. { All Rights Reserved. }
  18. { }
  19. { Contributor(s): }
  20. { Marcel van Brakel }
  21. { Olivier Sannier (obones) }
  22. { Matthias Thoma (mthoma) }
  23. { }
  24. {**************************************************************************************************}
  25. { }
  26. { This unit contains various classes and support routines for implementing synchronisation in }
  27. { multithreaded applications. This ranges from interlocked access to simple typed variables to }
  28. { wrapper classes for synchronisation primitives provided by the operating system }
  29. { (critical section, semaphore, mutex etc). It also includes three user defined classes to }
  30. { complement these. }
  31. { }
  32. {**************************************************************************************************}
  33. { }
  34. { Last modified: $Date:: $ }
  35. { Revision: $Rev:: $ }
  36. { Author: $Author:: $ }
  37. { }
  38. {**************************************************************************************************}
  39. unit JclSynch;
  40. {$I jcl.inc}
  41. interface
  42. uses
  43. {$IFDEF UNITVERSIONING}
  44. JclUnitVersioning,
  45. {$ENDIF UNITVERSIONING}
  46. {$IFDEF HAS_UNITSCOPE}
  47. {$IFDEF MSWINDOWS}
  48. Winapi.Windows, JclWin32,
  49. {$ENDIF MSWINDOWS}
  50. {$ELSE ~HAS_UNITSCOPE}
  51. {$IFDEF MSWINDOWS}
  52. Windows, JclWin32,
  53. {$ENDIF MSWINDOWS}
  54. {$ENDIF ~HAS_UNITSCOPE}
  55. JclBase;
  56. // Locked Integer manipulation
  57. //
  58. // Routines to manipulate simple typed variables in a thread safe manner
  59. function LockedAdd(var Target: Integer; Value: Integer): Integer; overload;
  60. function LockedCompareExchange(var Target: Integer; Exch, Comp: Integer): Integer; overload;
  61. function LockedCompareExchange(var Target: TObject; Exch, Comp: TObject): TObject; overload;
  62. function LockedCompareExchange(var Target: Pointer; Exch, Comp: Pointer): Pointer; overload;
  63. function LockedDec(var Target: Integer): Integer; overload;
  64. function LockedExchange(var Target: Integer; Value: Integer): Integer; overload;
  65. function LockedExchangeAdd(var Target: Integer; Value: Integer): Integer; overload;
  66. function LockedExchangeDec(var Target: Integer): Integer; overload;
  67. function LockedExchangeInc(var Target: Integer): Integer; overload;
  68. function LockedExchangeSub(var Target: Integer; Value: Integer): Integer; overload;
  69. function LockedInc(var Target: Integer): Integer; overload;
  70. function LockedSub(var Target: Integer; Value: Integer): Integer; overload;
  71. {$IFDEF CPU64}
  72. function LockedAdd(var Target: Int64; Value: Int64): Int64; overload;
  73. function LockedCompareExchange(var Target: Int64; Exch, Comp: Int64): Int64; overload;
  74. function LockedDec(var Target: Int64): Int64; overload;
  75. function LockedExchange(var Target: Int64; Value: Int64): Int64; overload;
  76. function LockedExchangeAdd(var Target: Int64; Value: Int64): Int64; overload;
  77. function LockedExchangeDec(var Target: Int64): Int64; overload;
  78. function LockedExchangeInc(var Target: Int64): Int64; overload;
  79. function LockedExchangeSub(var Target: Int64; Value: Int64): Int64; overload;
  80. function LockedInc(var Target: Int64): Int64; overload;
  81. function LockedSub(var Target: Int64; Value: Int64): Int64; overload;
  82. {$IFDEF BORLAND}
  83. {$IFNDEF COMPILER29_UP}
  84. function LockedDec(var Target: NativeInt): NativeInt; overload;
  85. function LockedInc(var Target: NativeInt): NativeInt; overload;
  86. {$ENDIF ~COMPILER29_UP}
  87. {$ENDIF BORLAND}
  88. {$ENDIF CPU64}
  89. // TJclDispatcherObject
  90. //
  91. // Base class for operating system provided synchronisation primitives
  92. type
  93. TJclWaitResult = (wrAbandoned, wrError, wrIoCompletion, wrSignaled, wrTimeout);
  94. TJclWaitHandle = THandle;
  95. TJclDispatcherObject = class(TObject)
  96. private
  97. FExisted: Boolean;
  98. FHandle: TJclWaitHandle;
  99. FName: string;
  100. public
  101. constructor Attach(AHandle: TJclWaitHandle);
  102. destructor Destroy; override;
  103. //function MsgWaitFor(const TimeOut: Cardinal): TJclWaitResult; Mask: DWORD): TJclWaitResult;
  104. //function MsgWaitForEx(const TimeOut: Cardinal): TJclWaitResult; Mask: DWORD): TJclWaitResult;
  105. function SignalAndWait(const Obj: TJclDispatcherObject; TimeOut: Cardinal;
  106. Alertable: Boolean): TJclWaitResult;
  107. function WaitAlertable(const TimeOut: Cardinal): TJclWaitResult;
  108. function WaitFor(const TimeOut: Cardinal): TJclWaitResult;
  109. function WaitForever: TJclWaitResult;
  110. property Existed: Boolean read FExisted;
  111. property Handle: TJclWaitHandle read FHandle;
  112. property Name: string read FName;
  113. end;
  114. // Wait functions
  115. //
  116. // Object enabled Wait functions (takes TJclDispatcher objects as parameter as
  117. // opposed to handles) mostly for convenience
  118. function WaitForMultipleObjects(const Objects: array of TJclDispatcherObject;
  119. WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
  120. function WaitAlertableForMultipleObjects(const Objects: array of TJclDispatcherObject;
  121. WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
  122. type
  123. TJclCriticalSection = class(TObject)
  124. private
  125. FCriticalSection: TRTLCriticalSection;
  126. public
  127. constructor Create; virtual;
  128. destructor Destroy; override;
  129. class procedure CreateAndEnter(var CS: TJclCriticalSection);
  130. procedure Enter;
  131. procedure Leave;
  132. end;
  133. TJclCriticalSectionEx = class(TJclCriticalSection)
  134. private
  135. FSpinCount: Cardinal;
  136. {$IFNDEF WINSCP}
  137. function GetSpinCount: Cardinal;
  138. procedure SetSpinCount(const Value: Cardinal);
  139. {$ENDIF ~WINSCP}
  140. public
  141. constructor Create; override;
  142. constructor CreateEx(SpinCount: Cardinal; NoFailEnter: Boolean); virtual;
  143. {$IFNDEF WINSCP}
  144. class function GetSpinTimeOut: Cardinal;
  145. class procedure SetSpinTimeOut(const Value: Cardinal);
  146. {$ENDIF ~WINSCP}
  147. function TryEnter: Boolean;
  148. {$IFNDEF WINSCP}
  149. property SpinCount: Cardinal read GetSpinCount write SetSpinCount;
  150. {$ENDIF ~WINSCP}
  151. end;
  152. TJclEvent = class(TJclDispatcherObject)
  153. public
  154. constructor Create(SecAttr: PSecurityAttributes; Manual, Signaled: Boolean; const Name: string);
  155. constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
  156. function Pulse: Boolean;
  157. function ResetEvent: Boolean;
  158. function SetEvent: Boolean;
  159. end;
  160. TJclWaitableTimer = class(TJclDispatcherObject)
  161. private
  162. FResume: Boolean;
  163. public
  164. constructor Create(SecAttr: PSecurityAttributes; Manual: Boolean; const Name: string);
  165. constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
  166. function Cancel: Boolean;
  167. function SetTimer(const DueTime: Int64; Period: Longint; Resume: Boolean): Boolean;
  168. function SetTimerApc(const DueTime: Int64; Period: Longint; Resume: Boolean; Apc: TFNTimerAPCRoutine; Arg: Pointer): Boolean;
  169. end;
  170. TJclSemaphore = class(TJclDispatcherObject)
  171. public
  172. constructor Create(SecAttr: PSecurityAttributes; Initial, Maximum: Longint; const Name: string);
  173. constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
  174. function Release(ReleaseCount: Longint): Boolean;
  175. function ReleasePrev(ReleaseCount: Longint; var PrevCount: Longint): Boolean;
  176. end;
  177. TJclMutex = class(TJclDispatcherObject)
  178. public
  179. constructor Create(SecAttr: PSecurityAttributes; InitialOwner: Boolean;
  180. const Name: string);
  181. constructor Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
  182. function Acquire(const TimeOut: Cardinal = INFINITE): Boolean;
  183. function Release: Boolean;
  184. end;
  185. POptexSharedInfo = ^TOptexSharedInfo;
  186. TOptexSharedInfo = record
  187. SpinCount: Integer; // number of times to try and enter the optex before
  188. // waiting on kernel event, 0 on single processor
  189. LockCount: Integer; // count of enter attempts
  190. ThreadId: Longword; // id of thread that owns the optex, 0 if free
  191. RecursionCount: Integer; // number of times the optex is owned, 0 if free
  192. end;
  193. {$IFNDEF WINSCP}
  194. TJclOptex = class(TObject)
  195. private
  196. FEvent: TJclEvent;
  197. FExisted: Boolean;
  198. FFileMapping: THandle;
  199. FName: string;
  200. FSharedInfo: POptexSharedInfo;
  201. function GetUniProcess: Boolean;
  202. function GetSpinCount: Integer;
  203. procedure SetSpinCount(Value: Integer);
  204. public
  205. constructor Create(const Name: string = ''; SpinCount: Integer = 4000);
  206. destructor Destroy; override;
  207. procedure Enter;
  208. procedure Leave;
  209. function TryEnter: Boolean;
  210. property Existed: Boolean read FExisted;
  211. property Name: string read FName;
  212. property SpinCount: Integer read GetSpinCount write SetSpinCount;
  213. property UniProcess: Boolean read GetUniProcess;
  214. end;
  215. {$ENDIF ~WINSCP}
  216. TMrewPreferred = (mpReaders, mpWriters, mpEqual);
  217. TMrewThreadInfo = record
  218. ThreadId: Longword; // client-id of thread
  219. RecursionCount: Integer; // number of times a thread accessed the mrew
  220. Reader: Boolean; // true if reader, false if writer
  221. end;
  222. TMrewThreadInfoArray = array of TMrewThreadInfo;
  223. TJclMultiReadExclusiveWrite = class(TObject)
  224. private
  225. FLock: TJclCriticalSection;
  226. FPreferred: TMrewPreferred;
  227. FSemReaders: TJclSemaphore;
  228. FSemWriters: TJclSemaphore;
  229. FState: Integer;
  230. FThreads: TMrewThreadInfoArray;
  231. FWaitingReaders: Integer;
  232. FWaitingWriters: Integer;
  233. procedure AddToThreadList(ThreadId: Longword; Reader: Boolean);
  234. procedure RemoveFromThreadList(Index: Integer);
  235. function FindThread(ThreadId: Longword): Integer;
  236. procedure ReleaseWaiters(WasReading: Boolean);
  237. protected
  238. procedure Release;
  239. public
  240. constructor Create(Preferred: TMrewPreferred);
  241. destructor Destroy; override;
  242. procedure BeginRead;
  243. procedure BeginWrite;
  244. procedure EndRead;
  245. procedure EndWrite;
  246. end;
  247. PMetSectSharedInfo = ^TMetSectSharedInfo;
  248. TMetSectSharedInfo = record
  249. Initialized: LongBool; // Is the metered section initialized?
  250. SpinLock: Longint; // Used to gain access to this structure
  251. ThreadsWaiting: Longint; // Count of threads waiting
  252. AvailableCount: Longint; // Available resource count
  253. MaximumCount: Longint; // Maximum resource count
  254. end;
  255. PMeteredSection = ^TMeteredSection;
  256. TMeteredSection = record
  257. Event: THandle; // Handle to a kernel event object
  258. FileMap: THandle; // Handle to memory mapped file
  259. SharedInfo: PMetSectSharedInfo;
  260. end;
  261. TJclMeteredSection = class(TObject)
  262. private
  263. FMetSect: PMeteredSection;
  264. procedure CloseMeteredSection;
  265. function InitMeteredSection(InitialCount, MaxCount: Longint; const Name: string; OpenOnly: Boolean): Boolean;
  266. function CreateMetSectEvent(const Name: string; OpenOnly: Boolean): Boolean;
  267. function CreateMetSectFileView(InitialCount, MaxCount: Longint; const Name: string; OpenOnly: Boolean): Boolean;
  268. protected
  269. procedure AcquireLock;
  270. procedure ReleaseLock;
  271. public
  272. constructor Create(InitialCount, MaxCount: Longint; const Name: string);
  273. constructor Open(const Name: string);
  274. destructor Destroy; override;
  275. function Enter(TimeOut: Longword): TJclWaitResult;
  276. function Leave(ReleaseCount: Longint): Boolean; overload;
  277. function Leave(ReleaseCount: Longint; out PrevCount: Longint): Boolean; overload;
  278. end;
  279. // Debugging
  280. //
  281. // Note that the following function and structure declarations are all offically
  282. // undocumented and, except for QueryCriticalSection, require Windows NT since
  283. // it is all part of the Windows NT Native API.
  284. { TODO -cTest : Test this structures }
  285. type
  286. TEventInfo = record
  287. EventType: Longint; // 0 = manual, otherwise auto
  288. Signaled: LongBool; // true is signaled
  289. end;
  290. TMutexInfo = record
  291. SignalState: Longint; // >0 = signaled, <0 = |SignalState| recurs. acquired
  292. Owned: ByteBool; // owned by thread
  293. Abandoned: ByteBool; // is abandoned?
  294. end;
  295. TSemaphoreCounts = record
  296. CurrentCount: Longint; // current semaphore count
  297. MaximumCount: Longint; // maximum semaphore count
  298. end;
  299. TTimerInfo = record
  300. Remaining: TLargeInteger; // 100ns intervals until signaled
  301. Signaled: ByteBool; // is signaled?
  302. end;
  303. function QueryCriticalSection(CS: TJclCriticalSection; var Info: TRTLCriticalSection): Boolean;
  304. { TODO -cTest : Test these 4 functions }
  305. function QueryEvent(Handle: THandle; var Info: TEventInfo): Boolean;
  306. function QueryMutex(Handle: THandle; var Info: TMutexInfo): Boolean;
  307. function QuerySemaphore(Handle: THandle; var Info: TSemaphoreCounts): Boolean;
  308. function QueryTimer(Handle: THandle; var Info: TTimerInfo): Boolean;
  309. type
  310. // Exceptions
  311. EJclWin32HandleObjectError = class(EJclWin32Error);
  312. EJclDispatcherObjectError = class(EJclWin32Error);
  313. EJclCriticalSectionError = class(EJclWin32Error);
  314. EJclEventError = class(EJclWin32Error);
  315. EJclWaitableTimerError = class(EJclWin32Error);
  316. EJclSemaphoreError = class(EJclWin32Error);
  317. EJclMutexError = class(EJclWin32Error);
  318. EJclMeteredSectionError = class(EJclError);
  319. function ValidateMutexName(const aName: string): string;
  320. {$IFDEF UNITVERSIONING}
  321. const
  322. UnitVersioning: TUnitVersionInfo = (
  323. RCSfile: '$URL$';
  324. Revision: '$Revision$';
  325. Date: '$Date$';
  326. LogPath: 'JCL\source\common';
  327. Extra: '';
  328. Data: nil
  329. );
  330. {$ENDIF UNITVERSIONING}
  331. implementation
  332. uses
  333. {$IFDEF HAS_UNITSCOPE}
  334. System.SysUtils,
  335. {$ELSE ~HAS_UNITSCOPE}
  336. SysUtils,
  337. {$ENDIF ~HAS_UNITSCOPE}
  338. {$IFNDEF WINSCP}JclLogic, JclRegistry,{$ELSE}Math,{$ENDIF ~WINSCP} JclResources,
  339. {$IFNDEF WINSCP}JclSysInfo,{$ENDIF ~WINSCP} JclStrings;
  340. const
  341. RegSessionManager = {HKLM\} 'SYSTEM\CurrentControlSet\Control\Session Manager';
  342. RegCritSecTimeout = {RegSessionManager\} 'CriticalSectionTimeout';
  343. // Locked Integer manipulation
  344. function LockedAdd(var Target: Integer; Value: Integer): Integer;
  345. asm
  346. {$IFDEF CPU32}
  347. // --> EAX Target
  348. // EDX Value
  349. // <-- EAX Result
  350. MOV ECX, EAX
  351. MOV EAX, EDX
  352. LOCK XADD [ECX], EAX
  353. ADD EAX, EDX
  354. {$ENDIF CPU32}
  355. {$IFDEF CPU64}
  356. // --> RCX Target
  357. // EDX Value
  358. // <-- EAX Result
  359. MOV EAX, EDX
  360. LOCK XADD [RCX], EAX
  361. ADD EAX, EDX
  362. {$ENDIF CPU64}
  363. end;
  364. function LockedCompareExchange(var Target: Integer; Exch, Comp: Integer): Integer;
  365. asm
  366. {$IFDEF CPU32}
  367. // --> EAX Target
  368. // EDX Exch
  369. // ECX Comp
  370. // <-- EAX Result
  371. XCHG EAX, ECX
  372. // EAX Comp
  373. // EDX Exch
  374. // ECX Target
  375. LOCK CMPXCHG [ECX], EDX
  376. {$ENDIF CPU32}
  377. {$IFDEF CPU64}
  378. // --> RCX Target
  379. // EDX Exch
  380. // R8 Comp
  381. // <-- EAX Result
  382. MOV RAX, R8
  383. // RCX Target
  384. // EDX Exch
  385. // RAX Comp
  386. LOCK CMPXCHG [RCX], EDX
  387. {$ENDIF CPU64}
  388. end;
  389. function LockedCompareExchange(var Target: Pointer; Exch, Comp: Pointer): Pointer;
  390. asm
  391. {$IFDEF CPU32}
  392. // --> EAX Target
  393. // EDX Exch
  394. // ECX Comp
  395. // <-- EAX Result
  396. XCHG EAX, ECX
  397. // EAX Comp
  398. // EDX Exch
  399. // ECX Target
  400. LOCK CMPXCHG [ECX], EDX
  401. {$ENDIF CPU32}
  402. {$IFDEF CPU64}
  403. // --> RCX Target
  404. // RDX Exch
  405. // R8 Comp
  406. // <-- RAX Result
  407. MOV RAX, R8
  408. // RCX Target
  409. // RDX Exch
  410. // RAX Comp
  411. LOCK CMPXCHG [RCX], RDX
  412. {$ENDIF CPU64}
  413. end;
  414. function LockedCompareExchange(var Target: TObject; Exch, Comp: TObject): TObject;
  415. asm
  416. {$IFDEF CPU32}
  417. // --> EAX Target
  418. // EDX Exch
  419. // ECX Comp
  420. // <-- EAX Result
  421. XCHG EAX, ECX
  422. // EAX Comp
  423. // EDX Exch
  424. // ECX Target
  425. LOCK CMPXCHG [ECX], EDX
  426. {$ENDIF CPU32}
  427. {$IFDEF CPU64}
  428. // --> RCX Target
  429. // RDX Exch
  430. // R8 Comp
  431. // <-- RAX Result
  432. MOV RAX, R8
  433. // --> RCX Target
  434. // RDX Exch
  435. // RAX Comp
  436. LOCK CMPXCHG [RCX], RDX
  437. {$ENDIF CPU64}
  438. end;
  439. function LockedDec(var Target: Integer): Integer;
  440. asm
  441. {$IFDEF CPU32}
  442. // --> EAX Target
  443. // <-- EAX Result
  444. MOV ECX, EAX
  445. MOV EAX, -1
  446. LOCK XADD [ECX], EAX
  447. DEC EAX
  448. {$ENDIF CPU32}
  449. {$IFDEF CPU64}
  450. // --> RCX Target
  451. // <-- EAX Result
  452. MOV EAX, -1
  453. LOCK XADD [RCX], EAX
  454. DEC EAX
  455. {$ENDIF CPU64}
  456. end;
  457. function LockedExchange(var Target: Integer; Value: Integer): Integer;
  458. asm
  459. {$IFDEF CPU32}
  460. // --> EAX Target
  461. // EDX Value
  462. // <-- EAX Result
  463. MOV ECX, EAX
  464. MOV EAX, EDX
  465. // ECX Target
  466. // EAX Value
  467. LOCK XCHG [ECX], EAX
  468. {$ENDIF CPU32}
  469. {$IFDEF CPU64}
  470. // --> RCX Target
  471. // EDX Value
  472. // <-- EAX Result
  473. MOV EAX, EDX
  474. // RCX Target
  475. // EAX Value
  476. LOCK XCHG [RCX], EAX
  477. {$ENDIF CPU64}
  478. end;
  479. function LockedExchangeAdd(var Target: Integer; Value: Integer): Integer;
  480. asm
  481. {$IFDEF CPU32}
  482. // --> EAX Target
  483. // EDX Value
  484. // <-- EAX Result
  485. MOV ECX, EAX
  486. MOV EAX, EDX
  487. // ECX Target
  488. // EAX Value
  489. LOCK XADD [ECX], EAX
  490. {$ENDIF CPU32}
  491. {$IFDEF CPU64}
  492. // --> RCX Target
  493. // EDX Value
  494. // <-- EAX Result
  495. MOV EAX, EDX
  496. // RCX Target
  497. // EAX Value
  498. LOCK XADD [RCX], EAX
  499. {$ENDIF CPU64}
  500. end;
  501. function LockedExchangeDec(var Target: Integer): Integer;
  502. asm
  503. {$IFDEF CPU32}
  504. // --> EAX Target
  505. // <-- EAX Result
  506. MOV ECX, EAX
  507. MOV EAX, -1
  508. LOCK XADD [ECX], EAX
  509. {$ENDIF CPU32}
  510. {$IFDEF CPU64}
  511. // --> RCX Target
  512. // <-- EAX Result
  513. MOV EAX, -1
  514. LOCK XADD [RCX], EAX
  515. {$ENDIF CPU64}
  516. end;
  517. function LockedExchangeInc(var Target: Integer): Integer;
  518. asm
  519. {$IFDEF CPU32}
  520. // --> EAX Target
  521. // <-- EAX Result
  522. MOV ECX, EAX
  523. MOV EAX, 1
  524. LOCK XADD [ECX], EAX
  525. {$ENDIF CPU32}
  526. {$IFDEF CPU64}
  527. // --> RCX Target
  528. // <-- EAX Result
  529. MOV EAX, 1
  530. LOCK XADD [RCX], EAX
  531. {$ENDIF CPU64}
  532. end;
  533. function LockedExchangeSub(var Target: Integer; Value: Integer): Integer;
  534. asm
  535. {$IFDEF CPU32}
  536. // --> EAX Target
  537. // EDX Value
  538. // <-- EAX Result
  539. MOV ECX, EAX
  540. NEG EDX
  541. MOV EAX, EDX
  542. // ECX Target
  543. // EAX -Value
  544. LOCK XADD [ECX], EAX
  545. {$ENDIF CPU32}
  546. {$IFDEF CPU64}
  547. // --> RCX Target
  548. // EDX Value
  549. // <-- EAX Result
  550. NEG EDX
  551. MOV EAX, EDX
  552. // RCX Target
  553. // EAX -Value
  554. LOCK XADD [RCX], EAX
  555. {$ENDIF CPU64}
  556. end;
  557. function LockedInc(var Target: Integer): Integer;
  558. asm
  559. {$IFDEF CPU32}
  560. // --> EAX Target
  561. // <-- EAX Result
  562. MOV ECX, EAX
  563. MOV EAX, 1
  564. LOCK XADD [ECX], EAX
  565. INC EAX
  566. {$ENDIF CPU32}
  567. {$IFDEF CPU64}
  568. // --> RCX Target
  569. // <-- EAX Result
  570. MOV EAX, 1
  571. LOCK XADD [RCX], EAX
  572. INC EAX
  573. {$ENDIF CPU64}
  574. end;
  575. function LockedSub(var Target: Integer; Value: Integer): Integer;
  576. asm
  577. {$IFDEF CPU32}
  578. // --> EAX Target
  579. // EDX Value
  580. // <-- EAX Result
  581. MOV ECX, EAX
  582. NEG EDX
  583. MOV EAX, EDX
  584. LOCK XADD [ECX], EAX
  585. ADD EAX, EDX
  586. {$ENDIF CPU32}
  587. {$IFDEF CPU64}
  588. // --> RCX Target
  589. // EDX Value
  590. // <-- EAX Result
  591. NEG EDX
  592. MOV EAX, EDX
  593. LOCK XADD [RCX], EAX
  594. ADD EAX, EDX
  595. {$ENDIF CPU64}
  596. end;
  597. {$IFDEF CPU64}
  598. // Locked Int64 manipulation
  599. function LockedAdd(var Target: Int64; Value: Int64): Int64;
  600. asm
  601. // --> RCX Target
  602. // RDX Value
  603. // <-- RAX Result
  604. MOV RAX, RDX
  605. LOCK XADD [RCX], RAX
  606. ADD RAX, RDX
  607. end;
  608. function LockedCompareExchange(var Target: Int64; Exch, Comp: Int64): Int64;
  609. asm
  610. // --> RCX Target
  611. // RDX Exch
  612. // R8 Comp
  613. // <-- RAX Result
  614. MOV RAX, R8
  615. LOCK CMPXCHG [RCX], RDX
  616. end;
  617. function LockedDec(var Target: Int64): Int64;
  618. asm
  619. // --> RCX Target
  620. // <-- RAX Result
  621. MOV RAX, -1
  622. LOCK XADD [RCX], RAX
  623. DEC RAX
  624. end;
  625. function LockedExchange(var Target: Int64; Value: Int64): Int64;
  626. asm
  627. // --> RCX Target
  628. // RDX Value
  629. // <-- RAX Result
  630. MOV RAX, RDX
  631. LOCK XCHG [RCX], RAX
  632. end;
  633. function LockedExchangeAdd(var Target: Int64; Value: Int64): Int64;
  634. asm
  635. // --> RCX Target
  636. // RDX Value
  637. // <-- RAX Result
  638. MOV RAX, RDX
  639. LOCK XADD [RCX], RAX
  640. end;
  641. function LockedExchangeDec(var Target: Int64): Int64;
  642. asm
  643. // --> RCX Target
  644. // <-- RAX Result
  645. MOV RAX, -1
  646. LOCK XADD [RCX], RAX
  647. end;
  648. function LockedExchangeInc(var Target: Int64): Int64;
  649. asm
  650. // --> RCX Target
  651. // <-- RAX Result
  652. MOV RAX, 1
  653. LOCK XADD [RCX], RAX
  654. end;
  655. function LockedExchangeSub(var Target: Int64; Value: Int64): Int64;
  656. asm
  657. // --> RCX Target
  658. // RDX Value
  659. // <-- RAX Result
  660. NEG RDX
  661. MOV RAX, RDX
  662. LOCK XADD [RCX], RAX
  663. end;
  664. function LockedInc(var Target: Int64): Int64;
  665. asm
  666. // --> RCX Target
  667. // <-- RAX Result
  668. MOV RAX, 1
  669. LOCK XADD [RCX], RAX
  670. INC RAX
  671. end;
  672. function LockedSub(var Target: Int64; Value: Int64): Int64;
  673. asm
  674. // --> RCX Target
  675. // RDX Value
  676. // <-- RAX Result
  677. NEG RDX
  678. MOV RAX, RDX
  679. LOCK XADD [RCX], RAX
  680. ADD RAX, RDX
  681. end;
  682. {$IFDEF BORLAND}
  683. {$IFNDEF COMPILER29_UP}
  684. function LockedDec(var Target: NativeInt): NativeInt;
  685. asm
  686. // --> RCX Target
  687. // <-- RAX Result
  688. MOV RAX, -1
  689. LOCK XADD [RCX], RAX
  690. DEC RAX
  691. end;
  692. function LockedInc(var Target: NativeInt): NativeInt;
  693. asm
  694. // --> RCX Target
  695. // <-- RAX Result
  696. MOV RAX, 1
  697. LOCK XADD [RCX], RAX
  698. INC RAX
  699. end;
  700. {$ENDIF ~COMPILER29_UP}
  701. {$ENDIF BORLAND}
  702. {$ENDIF CPU64}
  703. //=== { TJclDispatcherObject } ===============================================
  704. function MapSignalResult(const Ret: DWORD): TJclWaitResult;
  705. begin
  706. case Ret of
  707. WAIT_ABANDONED:
  708. Result := wrAbandoned;
  709. WAIT_OBJECT_0:
  710. Result := wrSignaled;
  711. WAIT_TIMEOUT:
  712. Result := wrTimeout;
  713. WAIT_IO_COMPLETION:
  714. Result := wrIoCompletion;
  715. WAIT_FAILED:
  716. Result := wrError;
  717. else
  718. Result := wrError;
  719. end;
  720. end;
  721. constructor TJclDispatcherObject.Attach(AHandle: TJclWaitHandle);
  722. begin
  723. inherited Create;
  724. FExisted := True;
  725. FHandle := AHandle;
  726. FName := '';
  727. end;
  728. destructor TJclDispatcherObject.Destroy;
  729. begin
  730. CloseHandle(FHandle);
  731. inherited Destroy;
  732. end;
  733. { TODO: Use RTDL Version of SignalObjectAndWait }
  734. function TJclDispatcherObject.SignalAndWait(const Obj: TJclDispatcherObject;
  735. TimeOut: Cardinal; Alertable: Boolean): TJclWaitResult;
  736. begin
  737. // Note: Do not make this method virtual! It's only available on NT 4 up...
  738. Result := MapSignalResult(Cardinal({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SignalObjectAndWait(Obj.Handle, Handle, TimeOut, Alertable)));
  739. end;
  740. function TJclDispatcherObject.WaitAlertable(const TimeOut: Cardinal): TJclWaitResult;
  741. begin
  742. Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObjectEx(FHandle, TimeOut, True));
  743. end;
  744. function TJclDispatcherObject.WaitFor(const TimeOut: Cardinal): TJclWaitResult;
  745. begin
  746. Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObject(FHandle, TimeOut));
  747. end;
  748. function TJclDispatcherObject.WaitForever: TJclWaitResult;
  749. begin
  750. Result := WaitFor(INFINITE);
  751. end;
  752. // Wait functions
  753. function WaitForMultipleObjects(const Objects: array of TJclDispatcherObject;
  754. WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
  755. var
  756. Handles: array of TJclWaitHandle;
  757. I, Count: Integer;
  758. begin
  759. Count := High(Objects) + 1;
  760. SetLength(Handles, Count);
  761. for I := 0 to Count - 1 do
  762. Handles[I] := Objects[I].Handle;
  763. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForMultipleObjects(Count, @Handles[0], WaitAll, TimeOut);
  764. end;
  765. function WaitAlertableForMultipleObjects(const Objects: array of TJclDispatcherObject;
  766. WaitAll: Boolean; TimeOut: Cardinal): Cardinal;
  767. var
  768. Handles: array of TJclWaitHandle;
  769. I, Count: Integer;
  770. begin
  771. Count := High(Objects) + 1;
  772. SetLength(Handles, Count);
  773. for I := 0 to Count - 1 do
  774. Handles[I] := Objects[I].Handle;
  775. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForMultipleObjectsEx(Count, @Handles[0], WaitAll, TimeOut, True);
  776. end;
  777. //=== { TJclCriticalSection } ================================================
  778. constructor TJclCriticalSection.Create;
  779. begin
  780. inherited Create;
  781. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InitializeCriticalSection(FCriticalSection);
  782. end;
  783. destructor TJclCriticalSection.Destroy;
  784. begin
  785. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.DeleteCriticalSection(FCriticalSection);
  786. inherited Destroy;
  787. end;
  788. class procedure TJclCriticalSection.CreateAndEnter(var CS: TJclCriticalSection);
  789. var
  790. NewCritSect: TJclCriticalSection;
  791. begin
  792. NewCritSect := TJclCriticalSection.Create;
  793. if LockedCompareExchange(Pointer(CS), Pointer(NewCritSect), nil) <> nil then
  794. begin
  795. // LoadInProgress was <> nil -> no exchange took place, free the CS
  796. NewCritSect.Free;
  797. end;
  798. CS.Enter;
  799. end;
  800. procedure TJclCriticalSection.Enter;
  801. begin
  802. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.EnterCriticalSection(FCriticalSection);
  803. end;
  804. procedure TJclCriticalSection.Leave;
  805. begin
  806. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.LeaveCriticalSection(FCriticalSection);
  807. end;
  808. //== { TJclCriticalSectionEx } ===============================================
  809. const
  810. DefaultCritSectSpinCount = 4000;
  811. constructor TJclCriticalSectionEx.Create;
  812. begin
  813. CreateEx(DefaultCritSectSpinCount, False);
  814. end;
  815. { TODO: Use RTDL Version of InitializeCriticalSectionAndSpinCount }
  816. constructor TJclCriticalSectionEx.CreateEx(SpinCount: Cardinal;
  817. NoFailEnter: Boolean);
  818. begin
  819. FSpinCount := SpinCount;
  820. if NoFailEnter then
  821. SpinCount := SpinCount or Cardinal($80000000);
  822. if not InitializeCriticalSectionAndSpinCount(FCriticalSection, SpinCount) then
  823. raise EJclCriticalSectionError.CreateRes(@RsSynchInitCriticalSection);
  824. end;
  825. {$IFNDEF WINSCP}
  826. function TJclCriticalSectionEx.GetSpinCount: Cardinal;
  827. begin
  828. // Spinning only makes sense on multiprocessor systems. On a single processor
  829. // system the thread would simply waste cycles while the owning thread is
  830. // suspended and thus cannot release the critical section.
  831. if ProcessorCount = 1 then
  832. Result := 0
  833. else
  834. Result := FSpinCount;
  835. end;
  836. class function TJclCriticalSectionEx.GetSpinTimeOut: Cardinal;
  837. begin
  838. Result := Cardinal(RegReadInteger(HKEY_LOCAL_MACHINE, RegSessionManager,
  839. RegCritSecTimeout));
  840. end;
  841. { TODO: Use RTLD version of SetCriticalSectionSpinCount }
  842. procedure TJclCriticalSectionEx.SetSpinCount(const Value: Cardinal);
  843. begin
  844. FSpinCount := SetCriticalSectionSpinCount(FCriticalSection, Value);
  845. end;
  846. class procedure TJclCriticalSectionEx.SetSpinTimeOut(const Value: Cardinal);
  847. begin
  848. RegWriteInteger(HKEY_LOCAL_MACHINE, RegSessionManager, RegCritSecTimeout,
  849. Integer(Value));
  850. end;
  851. {$ENDIF ~WINSCP}
  852. { TODO: Use RTLD version of TryEnterCriticalSection }
  853. function TJclCriticalSectionEx.TryEnter: Boolean;
  854. begin
  855. Result := TryEnterCriticalSection(FCriticalSection);
  856. end;
  857. //== { TJclEvent } ===========================================================
  858. constructor TJclEvent.Create(SecAttr: PSecurityAttributes; Manual, Signaled: Boolean; const Name: string);
  859. begin
  860. inherited Create;
  861. FName := Name;
  862. FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(SecAttr, Manual, Signaled, PChar(FName));
  863. if FHandle = 0 then
  864. raise EJclEventError.CreateRes(@RsSynchCreateEvent);
  865. FExisted := GetLastError = ERROR_ALREADY_EXISTS;
  866. end;
  867. constructor TJclEvent.Open(Access: Cardinal; Inheritable: Boolean;
  868. const Name: string);
  869. begin
  870. FName := Name;
  871. FExisted := True;
  872. FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenEvent(Access, Inheritable, PChar(Name));
  873. if FHandle = 0 then
  874. raise EJclEventError.CreateRes(@RsSynchOpenEvent);
  875. end;
  876. function TJclEvent.Pulse: Boolean;
  877. begin
  878. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.PulseEvent(FHandle);
  879. end;
  880. function TJclEvent.ResetEvent: Boolean;
  881. begin
  882. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ResetEvent(FHandle);
  883. end;
  884. function TJclEvent.SetEvent: Boolean;
  885. begin
  886. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetEvent(FHandle);
  887. end;
  888. //=== { TJclWaitableTimer } ==================================================
  889. { TODO: Use RTLD version of CreateWaitableTimer }
  890. constructor TJclWaitableTimer.Create(SecAttr: PSecurityAttributes;
  891. Manual: Boolean; const Name: string);
  892. begin
  893. FName := Name;
  894. FResume := False;
  895. FHandle := CreateWaitableTimer(SecAttr, Manual, PChar(Name));
  896. if FHandle = 0 then
  897. raise EJclWaitableTimerError.CreateRes(@RsSynchCreateWaitableTimer);
  898. FExisted := GetLastError = ERROR_ALREADY_EXISTS;
  899. end;
  900. { TODO: Use RTLD version of CancelWaitableTimer }
  901. function TJclWaitableTimer.Cancel: Boolean;
  902. begin
  903. Result := CancelWaitableTimer(FHandle);
  904. end;
  905. { TODO: Use RTLD version of OpenWaitableTimer }
  906. constructor TJclWaitableTimer.Open(Access: Cardinal; Inheritable: Boolean;
  907. const Name: string);
  908. begin
  909. FExisted := True;
  910. FName := Name;
  911. FResume := False;
  912. FHandle := OpenWaitableTimer(Access, Inheritable, PChar(Name));
  913. if FHandle = 0 then
  914. raise EJclWaitableTimerError.CreateRes(@RsSynchOpenWaitableTimer);
  915. end;
  916. { TODO: Use RTLD version of SetWaitableTimer }
  917. function TJclWaitableTimer.SetTimer(const DueTime: Int64; Period: Longint;
  918. Resume: Boolean): Boolean;
  919. var
  920. DT: Int64;
  921. begin
  922. DT := DueTime;
  923. FResume := Resume;
  924. Result := SetWaitableTimer(FHandle, DT, Period, nil, nil, FResume);
  925. end;
  926. { TODO -cHelp : OS restrictions }
  927. function TJclWaitableTimer.SetTimerApc(const DueTime: Int64; Period: Longint;
  928. Resume: Boolean; Apc: TFNTimerAPCRoutine; Arg: Pointer): Boolean;
  929. var
  930. DT: Int64;
  931. begin
  932. DT := DueTime;
  933. FResume := Resume;
  934. Result := RtdlSetWaitableTimer(FHandle, DT, Period, Apc, Arg, FResume);
  935. { TODO : Exception for Win9x, older WinNT? }
  936. // if not Result and (GetLastError = ERROR_CALL_NOT_IMPLEMENTED) then
  937. // RaiseLastOSError;
  938. end;
  939. //== { TJclSemaphore } =======================================================
  940. constructor TJclSemaphore.Create(SecAttr: PSecurityAttributes;
  941. Initial, Maximum: Integer; const Name: string);
  942. begin
  943. Assert((Initial >= 0) and (Maximum > 0));
  944. FName := Name;
  945. FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateSemaphore(SecAttr, Initial, Maximum, PChar(Name));
  946. if FHandle = 0 then
  947. raise EJclSemaphoreError.CreateRes(@RsSynchCreateSemaphore);
  948. FExisted := GetLastError = ERROR_ALREADY_EXISTS;
  949. end;
  950. constructor TJclSemaphore.Open(Access: Cardinal; Inheritable: Boolean;
  951. const Name: string);
  952. begin
  953. FName := Name;
  954. FExisted := True;
  955. FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenSemaphore(Access, Inheritable, PChar(Name));
  956. if FHandle = 0 then
  957. raise EJclSemaphoreError.CreateRes(@RsSynchOpenSemaphore);
  958. end;
  959. function TJclSemaphore.ReleasePrev(ReleaseCount: Longint;
  960. var PrevCount: Longint): Boolean;
  961. begin
  962. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseSemaphore(FHandle, ReleaseCount, @PrevCount);
  963. end;
  964. function TJclSemaphore.Release(ReleaseCount: Integer): Boolean;
  965. begin
  966. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseSemaphore(FHandle, ReleaseCount, nil);
  967. end;
  968. //=== { TJclMutex } ==========================================================
  969. function TJclMutex.Acquire(const TimeOut: Cardinal): Boolean;
  970. begin
  971. Result := WaitFor(TimeOut) = wrSignaled;
  972. end;
  973. constructor TJclMutex.Create(SecAttr: PSecurityAttributes; InitialOwner: Boolean; const Name: string);
  974. begin
  975. inherited Create;
  976. FName := Name;
  977. FHandle := JclWin32.CreateMutex(SecAttr, InitialOwner, PChar(Name));
  978. if FHandle = 0 then
  979. raise EJclMutexError.CreateRes(@RsSynchCreateMutex);
  980. FExisted := GetLastError = ERROR_ALREADY_EXISTS;
  981. end;
  982. constructor TJclMutex.Open(Access: Cardinal; Inheritable: Boolean; const Name: string);
  983. begin
  984. inherited Create;
  985. FName := Name;
  986. FExisted := True;
  987. FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenMutex(Access, Inheritable, PChar(Name));
  988. if FHandle = 0 then
  989. raise EJclMutexError.CreateRes(@RsSynchOpenMutex);
  990. end;
  991. function TJclMutex.Release: Boolean;
  992. begin
  993. Result := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ReleaseMutex(FHandle);
  994. end;
  995. {$IFNDEF WINSCP}
  996. //=== { TJclOptex } ==========================================================
  997. constructor TJclOptex.Create(const Name: string; SpinCount: Integer);
  998. begin
  999. FExisted := False;
  1000. FName := Name;
  1001. if Name = '' then
  1002. begin
  1003. // None shared optex, don't need filemapping, sharedinfo is local
  1004. FFileMapping := 0;
  1005. FEvent := TJclEvent.Create(nil, False, False, '');
  1006. FSharedInfo := AllocMem(SizeOf(TOptexSharedInfo));
  1007. end
  1008. else
  1009. begin
  1010. // Shared optex, event protects access to sharedinfo. Creation of filemapping
  1011. // doesn't need protection as it will automatically "open" instead of "create"
  1012. // if another process already created it.
  1013. FEvent := TJclEvent.Create(nil, False, False, 'Optex_Event_' + Name);
  1014. FExisted := FEvent.Existed;
  1015. FFileMapping := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE,
  1016. 0, SizeOf(TOptexSharedInfo), PChar('Optex_MMF_' + Name));
  1017. Assert(FFileMapping <> 0);
  1018. FSharedInfo := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.MapViewOfFile(FFileMapping, FILE_MAP_WRITE, 0, 0, 0);
  1019. Assert(FSharedInfo <> nil);
  1020. end;
  1021. SetSpinCount(SpinCount);
  1022. end;
  1023. destructor TJclOptex.Destroy;
  1024. begin
  1025. FreeAndNil(FEvent);
  1026. if UniProcess then
  1027. FreeMem(FSharedInfo)
  1028. else
  1029. begin
  1030. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.UnmapViewOfFile(FSharedInfo);
  1031. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FFileMapping);
  1032. end;
  1033. inherited Destroy;
  1034. end;
  1035. procedure TJclOptex.Enter;
  1036. var
  1037. ThreadId: Longword;
  1038. begin
  1039. if TryEnter then
  1040. Exit;
  1041. ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
  1042. if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedIncrement(FSharedInfo^.LockCount) = 1 then
  1043. begin
  1044. // Optex was unowned
  1045. FSharedInfo^.ThreadId := ThreadId;
  1046. FSharedInfo^.RecursionCount := 1;
  1047. end
  1048. else
  1049. begin
  1050. if FSharedInfo^.ThreadId = ThreadId then
  1051. begin
  1052. // We already owned it, increase ownership count
  1053. Inc(FSharedInfo^.RecursionCount)
  1054. end
  1055. else
  1056. begin
  1057. // Optex is owner by someone else, wait for it to be released and then
  1058. // immediately take ownership
  1059. FEvent.WaitForever;
  1060. FSharedInfo^.ThreadId := ThreadId;
  1061. FSharedInfo^.RecursionCount := 1;
  1062. end;
  1063. end;
  1064. end;
  1065. function TJclOptex.GetSpinCount: Integer;
  1066. begin
  1067. Result := FSharedInfo^.SpinCount;
  1068. end;
  1069. function TJclOptex.GetUniProcess: Boolean;
  1070. begin
  1071. Result := FFileMapping = 0;
  1072. end;
  1073. procedure TJclOptex.Leave;
  1074. begin
  1075. Dec(FSharedInfo^.RecursionCount);
  1076. if FSharedInfo^.RecursionCount > 0 then
  1077. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedDecrement(FSharedInfo^.LockCount)
  1078. else
  1079. begin
  1080. FSharedInfo^.ThreadId := 0;
  1081. if {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedDecrement(FSharedInfo^.LockCount) > 0 then
  1082. FEvent.SetEvent;
  1083. end;
  1084. end;
  1085. procedure TJclOptex.SetSpinCount(Value: Integer);
  1086. begin
  1087. if Value < 0 then
  1088. Value := DefaultCritSectSpinCount;
  1089. // Spinning only makes sense on multiprocessor systems
  1090. if ProcessorCount > 1 then
  1091. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(Integer(FSharedInfo^.SpinCount), Value);
  1092. end;
  1093. function TJclOptex.TryEnter: Boolean;
  1094. var
  1095. ThreadId: Longword;
  1096. ThreadOwnsOptex: Boolean;
  1097. SpinCount: Integer;
  1098. begin
  1099. ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
  1100. SpinCount := FSharedInfo^.SpinCount;
  1101. repeat
  1102. //ThreadOwnsOptex := InterlockedCompareExchange(Pointer(FSharedInfo^.LockCount),
  1103. // Pointer(1), Pointer(0)) = Pointer(0); // not available on win95
  1104. ThreadOwnsOptex := LockedCompareExchange(FSharedInfo^.LockCount, 1, 0) = 0;
  1105. if ThreadOwnsOptex then
  1106. begin
  1107. // Optex was unowned
  1108. FSharedInfo^.ThreadId := ThreadId;
  1109. FSharedInfo^.RecursionCount := 1;
  1110. end
  1111. else
  1112. begin
  1113. if FSharedInfo^.ThreadId = ThreadId then
  1114. begin
  1115. // We already owned the Optex
  1116. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedIncrement(FSharedInfo^.LockCount);
  1117. Inc(FSharedInfo^.RecursionCount);
  1118. ThreadOwnsOptex := True;
  1119. end;
  1120. end;
  1121. Dec(SpinCount);
  1122. until ThreadOwnsOptex or (SpinCount <= 0);
  1123. Result := ThreadOwnsOptex;
  1124. end;
  1125. {$ENDIF ~WINSCP}
  1126. //=== { TJclMultiReadExclusiveWrite } ========================================
  1127. constructor TJclMultiReadExclusiveWrite.Create(Preferred: TMrewPreferred);
  1128. begin
  1129. inherited Create;
  1130. FLock := TJclCriticalSection.Create;
  1131. FPreferred := Preferred;
  1132. FSemReaders := TJclSemaphore.Create(nil, 0, MaxInt, '');
  1133. FSemWriters := TJclSemaphore.Create(nil, 0, MaxInt, '');
  1134. SetLength(FThreads, 0);
  1135. FState := 0;
  1136. FWaitingReaders := 0;
  1137. FWaitingWriters := 0;
  1138. end;
  1139. destructor TJclMultiReadExclusiveWrite.Destroy;
  1140. begin
  1141. FreeAndNil(FSemReaders);
  1142. FreeAndNil(FSemWriters);
  1143. FreeAndNil(FLock);
  1144. inherited Destroy;
  1145. end;
  1146. procedure TJclMultiReadExclusiveWrite.AddToThreadList(ThreadId: Longword;
  1147. Reader: Boolean);
  1148. var
  1149. L: Integer;
  1150. begin
  1151. // Caller must own lock
  1152. L := Length(FThreads);
  1153. SetLength(FThreads, L + 1);
  1154. FThreads[L].ThreadId := ThreadId;
  1155. FThreads[L].RecursionCount := 1;
  1156. FThreads[L].Reader := Reader;
  1157. end;
  1158. procedure TJclMultiReadExclusiveWrite.BeginRead;
  1159. var
  1160. ThreadId: Longword;
  1161. Index: Integer;
  1162. MustWait: Boolean;
  1163. begin
  1164. MustWait := False;
  1165. ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
  1166. FLock.Enter;
  1167. try
  1168. Index := FindThread(ThreadId);
  1169. if Index >= 0 then
  1170. begin
  1171. // Thread is on threadslist so it is already reading
  1172. Inc(FThreads[Index].RecursionCount);
  1173. end
  1174. else
  1175. begin
  1176. // Request to read (first time)
  1177. AddToThreadList(ThreadId, True);
  1178. if FState >= 0 then
  1179. begin
  1180. // MREW is unowned or only readers. If there are no waiting writers or
  1181. // readers are preferred then allow thread to continue, otherwise it must
  1182. // wait it's turn
  1183. if (FPreferred = mpReaders) or (FWaitingWriters = 0) then
  1184. Inc(FState)
  1185. else
  1186. begin
  1187. Inc(FWaitingReaders);
  1188. MustWait := True;
  1189. end;
  1190. end
  1191. else
  1192. begin
  1193. // MREW is owner by a writer, must wait
  1194. Inc(FWaitingReaders);
  1195. MustWait := True;
  1196. end;
  1197. end;
  1198. finally
  1199. FLock.Leave;
  1200. end;
  1201. if MustWait then
  1202. FSemReaders.WaitForever;
  1203. end;
  1204. procedure TJclMultiReadExclusiveWrite.BeginWrite;
  1205. var
  1206. ThreadId: Longword;
  1207. Index: Integer;
  1208. MustWait: Boolean;
  1209. begin
  1210. MustWait := False;
  1211. FLock.Enter;
  1212. try
  1213. ThreadId := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.GetCurrentThreadId;
  1214. Index := FindThread(ThreadId);
  1215. if Index < 0 then
  1216. begin
  1217. // Request to write (first time)
  1218. AddToThreadList(ThreadId, False);
  1219. if FState = 0 then
  1220. begin
  1221. // MREW is unowned so start writing
  1222. FState := -1;
  1223. end
  1224. else
  1225. begin
  1226. // MREW is owner, must wait
  1227. Inc(FWaitingWriters);
  1228. MustWait := True;
  1229. end;
  1230. end
  1231. else
  1232. begin
  1233. if FThreads[Index].Reader then
  1234. begin
  1235. // Request to write while reading
  1236. Inc(FThreads[Index].RecursionCount);
  1237. FThreads[Index].Reader := False;
  1238. Dec(FState);
  1239. if FState = 0 then
  1240. begin
  1241. // MREW is unowned so start writing
  1242. FState := -1;
  1243. end
  1244. else
  1245. begin
  1246. // MREW is owned, must wait
  1247. MustWait := True;
  1248. Inc(FWaitingWriters);
  1249. end;
  1250. end
  1251. else
  1252. // Requesting to write while already writing
  1253. Inc(FThreads[Index].RecursionCount);
  1254. end;
  1255. finally
  1256. FLock.Leave;
  1257. end;
  1258. if MustWait then
  1259. FSemWriters.WaitFor(INFINITE);
  1260. end;
  1261. procedure TJclMultiReadExclusiveWrite.EndRead;
  1262. begin
  1263. Release;
  1264. end;
  1265. procedure TJclMultiReadExclusiveWrite.EndWrite;
  1266. begin
  1267. Release;
  1268. end;
  1269. function TJclMultiReadExclusiveWrite.FindThread(ThreadId: Longword): Integer;
  1270. var
  1271. I: Integer;
  1272. begin
  1273. // Caller must lock
  1274. Result := -1;
  1275. for I := 0 to Length(FThreads) - 1 do
  1276. if FThreads[I].ThreadId = ThreadId then
  1277. begin
  1278. Result := I;
  1279. Exit;
  1280. end;
  1281. end;
  1282. procedure TJclMultiReadExclusiveWrite.Release;
  1283. var
  1284. ThreadId: Longword;
  1285. Index: Integer;
  1286. WasReading: Boolean;
  1287. begin
  1288. ThreadId := GetCurrentThreadId;
  1289. FLock.Enter;
  1290. try
  1291. Index := FindThread(ThreadId);
  1292. if Index >= 0 then
  1293. begin
  1294. Dec(FThreads[Index].RecursionCount);
  1295. if FThreads[Index].RecursionCount = 0 then
  1296. begin
  1297. WasReading := FThreads[Index].Reader;
  1298. if WasReading then
  1299. Dec(FState)
  1300. else
  1301. FState := 0;
  1302. RemoveFromThreadList(Index);
  1303. if FState = 0 then
  1304. ReleaseWaiters(WasReading);
  1305. end;
  1306. end;
  1307. finally
  1308. FLock.Leave;
  1309. end;
  1310. end;
  1311. procedure TJclMultiReadExclusiveWrite.ReleaseWaiters(WasReading: Boolean);
  1312. var
  1313. ToRelease: TMrewPreferred;
  1314. begin
  1315. // Caller must Lock
  1316. ToRelease := mpEqual;
  1317. case FPreferred of
  1318. mpReaders:
  1319. if FWaitingReaders > 0 then
  1320. ToRelease := mpReaders
  1321. else
  1322. if FWaitingWriters > 0 then
  1323. ToRelease := mpWriters;
  1324. mpWriters:
  1325. if FWaitingWriters > 0 then
  1326. ToRelease := mpWriters
  1327. else
  1328. if FWaitingReaders > 0 then
  1329. ToRelease := mpReaders;
  1330. mpEqual:
  1331. if WasReading then
  1332. begin
  1333. if FWaitingWriters > 0 then
  1334. ToRelease := mpWriters
  1335. else
  1336. if FWaitingReaders > 0 then
  1337. ToRelease := mpReaders;
  1338. end
  1339. else
  1340. begin
  1341. if FWaitingReaders > 0 then
  1342. ToRelease := mpReaders
  1343. else
  1344. if FWaitingWriters > 0 then
  1345. ToRelease := mpWriters;
  1346. end;
  1347. end;
  1348. case ToRelease of
  1349. mpReaders:
  1350. begin
  1351. FState := FWaitingReaders;
  1352. FWaitingReaders := 0;
  1353. FSemReaders.Release(FState);
  1354. end;
  1355. mpWriters:
  1356. begin
  1357. FState := -1;
  1358. Dec(FWaitingWriters);
  1359. FSemWriters.Release(1);
  1360. end;
  1361. mpEqual:
  1362. // no waiters
  1363. end;
  1364. end;
  1365. procedure TJclMultiReadExclusiveWrite.RemoveFromThreadList(Index: Integer);
  1366. var
  1367. L: Integer;
  1368. begin
  1369. // Caller must Lock
  1370. L := Length(FThreads);
  1371. if Index < (L - 1) then
  1372. Move(FThreads[Index + 1], FThreads[Index], SizeOf(TMrewThreadInfo) * (L - Index - 1));
  1373. SetLength(FThreads, L - 1);
  1374. end;
  1375. //=== { TJclMeteredSection } =================================================
  1376. const
  1377. MAX_METSECT_NAMELEN = 128;
  1378. constructor TJclMeteredSection.Create(InitialCount, MaxCount: Integer; const Name: string);
  1379. begin
  1380. if (MaxCount < 1) or (InitialCount > MaxCount) or (InitialCount < 0) or
  1381. (Length(Name) > MAX_METSECT_NAMELEN) then
  1382. raise EJclMeteredSectionError.CreateRes(@RsMetSectInvalidParameter);
  1383. FMetSect := PMeteredSection(AllocMem(SizeOf(TMeteredSection)));
  1384. if FMetSect <> nil then
  1385. begin
  1386. if not InitMeteredSection(InitialCount, MaxCount, Name, False) then
  1387. begin
  1388. CloseMeteredSection;
  1389. FMetSect := nil;
  1390. raise EJclMeteredSectionError.CreateRes(@RsMetSectInitialize);
  1391. end;
  1392. end;
  1393. end;
  1394. constructor TJclMeteredSection.Open(const Name: string);
  1395. begin
  1396. FMetSect := nil;
  1397. if Name = '' then
  1398. raise EJclMeteredSectionError.CreateRes(@RsMetSectNameEmpty);
  1399. FMetSect := PMeteredSection(AllocMem(SizeOf(TMeteredSection)));
  1400. Assert(FMetSect <> nil);
  1401. if not InitMeteredSection(0, 0, Name, True) then
  1402. begin
  1403. CloseMeteredSection;
  1404. FMetSect := nil;
  1405. raise EJclMeteredSectionError.CreateRes(@RsMetSectInitialize);
  1406. end;
  1407. end;
  1408. destructor TJclMeteredSection.Destroy;
  1409. begin
  1410. CloseMeteredSection;
  1411. inherited Destroy;
  1412. end;
  1413. procedure TJclMeteredSection.AcquireLock;
  1414. begin
  1415. while {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(FMetSect^.SharedInfo^.SpinLock, 1) <> 0 do
  1416. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.Sleep(0);
  1417. end;
  1418. procedure TJclMeteredSection.CloseMeteredSection;
  1419. begin
  1420. if FMetSect <> nil then
  1421. begin
  1422. if FMetSect^.SharedInfo <> nil then
  1423. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.UnmapViewOfFile(FMetSect^.SharedInfo);
  1424. if FMetSect^.FileMap <> 0 then
  1425. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FMetSect^.FileMap);
  1426. if FMetSect^.Event <> 0 then
  1427. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CloseHandle(FMetSect^.Event);
  1428. FreeMem(FMetSect);
  1429. end;
  1430. end;
  1431. function TJclMeteredSection.CreateMetSectEvent(const Name: string; OpenOnly: Boolean): Boolean;
  1432. var
  1433. FullName: string;
  1434. begin
  1435. if Name = '' then
  1436. FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(nil, False, False, nil)
  1437. else
  1438. begin
  1439. FullName := 'JCL_MSECT_EVT_' + Name;
  1440. if OpenOnly then
  1441. FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenEvent(0, False, PChar(FullName))
  1442. else
  1443. FMetSect^.Event := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateEvent(nil, False, False, PChar(FullName));
  1444. end;
  1445. Result := FMetSect^.Event <> 0;
  1446. end;
  1447. function TJclMeteredSection.CreateMetSectFileView(InitialCount, MaxCount: Longint;
  1448. const Name: string; OpenOnly: Boolean): Boolean;
  1449. var
  1450. FullName: string;
  1451. LastError: DWORD;
  1452. begin
  1453. Result := False;
  1454. if Name = '' then
  1455. FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TMetSectSharedInfo), nil)
  1456. else
  1457. begin
  1458. FullName := 'JCL_MSECT_MMF_' + Name;
  1459. if OpenOnly then
  1460. FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.OpenFileMapping(0, False, PChar(FullName))
  1461. else
  1462. FMetSect^.FileMap := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TMetSectSharedInfo), PChar(FullName));
  1463. end;
  1464. if FMetSect^.FileMap <> 0 then
  1465. begin
  1466. LastError := GetLastError;
  1467. FMetSect^.SharedInfo := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.MapViewOfFile(FMetSect^.FileMap, FILE_MAP_WRITE, 0, 0, 0);
  1468. if FMetSect^.SharedInfo <> nil then
  1469. begin
  1470. if LastError = ERROR_ALREADY_EXISTS then
  1471. while not FMetSect^.SharedInfo^.Initialized do Sleep(0)
  1472. else
  1473. begin
  1474. FMetSect^.SharedInfo^.SpinLock := 0;
  1475. FMetSect^.SharedInfo^.ThreadsWaiting := 0;
  1476. FMetSect^.SharedInfo^.AvailableCount := InitialCount;
  1477. FMetSect^.SharedInfo^.MaximumCount := MaxCount;
  1478. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(Integer(FMetSect^.SharedInfo^.Initialized), 1);
  1479. end;
  1480. Result := True;
  1481. end;
  1482. end;
  1483. end;
  1484. function TJclMeteredSection.Enter(TimeOut: Longword): TJclWaitResult;
  1485. begin
  1486. Result := wrSignaled;
  1487. while Result = wrSignaled do
  1488. begin
  1489. AcquireLock;
  1490. try
  1491. if FMetSect^.SharedInfo^.AvailableCount >= 1 then
  1492. begin
  1493. Dec(FMetSect^.SharedInfo^.AvailableCount);
  1494. Result := MapSignalResult(WAIT_OBJECT_0);
  1495. Exit;
  1496. end;
  1497. Inc(FMetSect^.SharedInfo^.ThreadsWaiting);
  1498. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.ResetEvent(FMetSect^.Event);
  1499. finally
  1500. ReleaseLock;
  1501. end;
  1502. Result := MapSignalResult({$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.WaitForSingleObject(FMetSect^.Event, TimeOut));
  1503. end;
  1504. end;
  1505. function TJclMeteredSection.InitMeteredSection(InitialCount, MaxCount: Longint;
  1506. const Name: string; OpenOnly: Boolean): Boolean;
  1507. begin
  1508. Result := False;
  1509. if CreateMetSectEvent(Name, OpenOnly) then
  1510. Result := CreateMetSectFileView(InitialCount, MaxCount, Name, OpenOnly);
  1511. end;
  1512. function TJclMeteredSection.Leave(ReleaseCount: Integer; out PrevCount: Integer): Boolean;
  1513. var
  1514. Count: Integer;
  1515. begin
  1516. Result := False;
  1517. AcquireLock;
  1518. try
  1519. PrevCount := FMetSect^.SharedInfo^.AvailableCount;
  1520. if (ReleaseCount < 0) or
  1521. (FMetSect^.SharedInfo^.AvailableCount + ReleaseCount > FMetSect^.SharedInfo^.MaximumCount) then
  1522. begin
  1523. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetLastError(ERROR_INVALID_PARAMETER);
  1524. Exit;
  1525. end;
  1526. Inc(FMetSect^.SharedInfo^.AvailableCount, ReleaseCount);
  1527. ReleaseCount := Min(ReleaseCount, FMetSect^.SharedInfo^.ThreadsWaiting);
  1528. if FMetSect^.SharedInfo^.ThreadsWaiting > 0 then
  1529. begin
  1530. for Count := 0 to ReleaseCount - 1 do
  1531. begin
  1532. Dec(FMetSect^.SharedInfo^.ThreadsWaiting);
  1533. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.SetEvent(FMetSect^.Event);
  1534. end;
  1535. end;
  1536. finally
  1537. ReleaseLock;
  1538. end;
  1539. Result := True;
  1540. end;
  1541. function TJclMeteredSection.Leave(ReleaseCount: Integer): Boolean;
  1542. var
  1543. Previous: Longint;
  1544. begin
  1545. Result := Leave(ReleaseCount, Previous);
  1546. end;
  1547. procedure TJclMeteredSection.ReleaseLock;
  1548. begin
  1549. {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.InterlockedExchange(FMetSect^.SharedInfo^.SpinLock, 0);
  1550. end;
  1551. //=== Debugging ==============================================================
  1552. function QueryCriticalSection(CS: TJclCriticalSection; var Info: TRTLCriticalSection): Boolean;
  1553. begin
  1554. Result := CS <> nil;
  1555. if Result then
  1556. Info := CS.FCriticalSection;
  1557. end;
  1558. // Native API functions
  1559. // http://undocumented.ntinternals.net/
  1560. { TODO: RTLD version }
  1561. type
  1562. TNtQueryProc = function (Handle: THandle; InfoClass: Byte; Info: Pointer;
  1563. Len: Longint; ResLen: PLongint): Longint; stdcall;
  1564. var
  1565. _QueryEvent: TNtQueryProc = nil;
  1566. _QueryMutex: TNtQueryProc = nil;
  1567. _QuerySemaphore: TNtQueryProc = nil;
  1568. _QueryTimer: TNtQueryProc = nil;
  1569. function CallQueryProc(var P: TNtQueryProc; const Name: string; Handle: THandle;
  1570. Info: Pointer; InfoSize: Longint): Boolean;
  1571. var
  1572. NtDll: THandle;
  1573. Status: Longint;
  1574. begin
  1575. Result := False;
  1576. if @P = nil then
  1577. begin
  1578. NtDll := GetModuleHandle(PChar('ntdll.dll'));
  1579. if NtDll <> 0 then
  1580. @P := GetProcAddress(NtDll, PChar(Name));
  1581. end;
  1582. if @P <> nil then
  1583. begin
  1584. Status := P(Handle, 0, Info, InfoSize, nil);
  1585. Result := (Status and $80000000) = 0;
  1586. end;
  1587. end;
  1588. function QueryEvent(Handle: THandle; var Info: TEventInfo): Boolean;
  1589. begin
  1590. Result := CallQueryProc(_QueryEvent, 'NtQueryEvent', Handle, @Info, SizeOf(Info));
  1591. end;
  1592. function QueryMutex(Handle: THandle; var Info: TMutexInfo): Boolean;
  1593. begin
  1594. Result := CallQueryProc(_QueryMutex, 'NtQueryMutex', Handle, @Info, SizeOf(Info));
  1595. end;
  1596. function QuerySemaphore(Handle: THandle; var Info: TSemaphoreCounts): Boolean;
  1597. begin
  1598. Result := CallQueryProc(_QuerySemaphore, 'NtQuerySemaphore', Handle, @Info, SizeOf(Info));
  1599. end;
  1600. function QueryTimer(Handle: THandle; var Info: TTimerInfo): Boolean;
  1601. begin
  1602. Result := CallQueryProc(_QueryTimer, 'NtQueryTimer', Handle, @Info, SizeOf(Info));
  1603. end;
  1604. function ValidateMutexName(const aName: string): string;
  1605. const cMutexMaxName = 200;
  1606. begin
  1607. if Length(aName) > cMutexMaxName then
  1608. Result := Copy (aName, Length(aName)-cMutexMaxName, cMutexMaxName)
  1609. else
  1610. Result := aName;
  1611. Result := StrReplaceChar(Result, '\', '_');
  1612. end;
  1613. {$IFDEF UNITVERSIONING}
  1614. initialization
  1615. RegisterUnitVersion(HInstance, UnitVersioning);
  1616. finalization
  1617. UnregisterUnitVersion(HInstance);
  1618. {$ENDIF UNITVERSIONING}
  1619. end.