systhrds.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2002 by Peter Vreman,
  5. member of the Free Pascal development team.
  6. Win32 threading support implementation
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$mode objfpc}
  14. unit systhrds;
  15. interface
  16. {$S-}
  17. type
  18. { the fields of this record are os dependent }
  19. { and they shouldn't be used in a program }
  20. { only the type TCriticalSection is important }
  21. PRTLCriticalSection = ^TRTLCriticalSection;
  22. TRTLCriticalSection = packed record
  23. DebugInfo : pointer;
  24. LockCount : longint;
  25. RecursionCount : longint;
  26. OwningThread : DWord;
  27. LockSemaphore : DWord;
  28. Reserved : DWord;
  29. end;
  30. { Include generic thread interface }
  31. {$i threadh.inc}
  32. implementation
  33. function SysGetCurrentThreadId : dword;forward;
  34. {*****************************************************************************
  35. Generic overloaded
  36. *****************************************************************************}
  37. { Include generic overloaded routines }
  38. {$i thread.inc}
  39. {*****************************************************************************
  40. Local WINApi imports
  41. *****************************************************************************}
  42. const
  43. { GlobalAlloc, GlobalFlags }
  44. GMEM_FIXED = 0;
  45. GMEM_ZEROINIT = 64;
  46. function TlsAlloc : DWord;
  47. stdcall;external 'kernel32' name 'TlsAlloc';
  48. function TlsGetValue(dwTlsIndex : DWord) : pointer;
  49. stdcall;external 'kernel32' name 'TlsGetValue';
  50. function TlsSetValue(dwTlsIndex : DWord;lpTlsValue : pointer) : LongBool;
  51. stdcall;external 'kernel32' name 'TlsSetValue';
  52. function TlsFree(dwTlsIndex : DWord) : LongBool;
  53. stdcall;external 'kernel32' name 'TlsFree';
  54. function CreateThread(lpThreadAttributes : pointer;
  55. dwStackSize : DWord; lpStartAddress : pointer;lpParameter : pointer;
  56. dwCreationFlags : DWord;var lpThreadId : DWord) : Dword;
  57. stdcall;external 'kernel32' name 'CreateThread';
  58. procedure ExitThread(dwExitCode : DWord);
  59. stdcall;external 'kernel32' name 'ExitThread';
  60. function GlobalAlloc(uFlags:DWord; dwBytes:DWORD):Pointer;
  61. stdcall;external 'kernel32' name 'GlobalAlloc';
  62. function GlobalFree(hMem : Pointer):Pointer; stdcall;external 'kernel32' name 'GlobalFree';
  63. procedure Sleep(dwMilliseconds: DWord); stdcall;external 'kernel32' name 'Sleep';
  64. function WinSuspendThread (threadHandle : dword) : dword; stdcall;external 'kernel32' name 'SuspendThread';
  65. function WinResumeThread (threadHandle : dword) : dword; stdcall;external 'kernel32' name 'ResumeThread';
  66. function TerminateThread (threadHandle : dword; var exitCode : dword) : boolean; stdcall;external 'kernel32' name 'TerminateThread';
  67. function GetLastError : dword; stdcall;external 'kernel32' name 'GetLastError';
  68. function WaitForSingleObject (hHandle,Milliseconds: dword): dword; stdcall;external 'kernel32' name 'WaitForSingleObject';
  69. function WinThreadSetPriority (threadHandle : dword; Prio: longint): boolean; stdcall;external 'kernel32' name 'SetThreadPriority';
  70. function WinThreadGetPriority (threadHandle : dword): Integer; stdcall;external 'kernel32' name 'GetThreadPriority';
  71. function WinGetCurrentThreadId : dword; stdcall;external 'kernel32' name 'GetCurrentThread';
  72. function CreateEvent(lpEventAttributes:pointer;bManualReset:longbool;bInitialState:longbool;lpName:pchar):CARDINAL; stdcall; external 'kernel32' name 'CreateEventA';
  73. function CloseHandle(hObject:CARDINAL):LONGBOOL; stdcall; external 'kernel32' name 'CloseHandle';
  74. function ResetEvent(hEvent:CARDINAL):LONGBOOL; stdcall; external 'kernel32' name 'ResetEvent';
  75. function SetEvent(hEvent:CARDINAL):LONGBOOL; stdcall; external 'kernel32' name 'SetEvent';
  76. function PulseEvent(hEvent:THANDLE):CARDINAL {WINBOOL}; stdcall; external 'kernel32' name 'PulseEvent';
  77. CONST
  78. WAIT_OBJECT_0 = 0;
  79. WAIT_ABANDONED_0 = $80;
  80. WAIT_TIMEOUT = $102;
  81. WAIT_IO_COMPLETION = $c0;
  82. WAIT_ABANDONED = $80;
  83. WAIT_FAILED = $ffffffff;
  84. {*****************************************************************************
  85. Threadvar support
  86. *****************************************************************************}
  87. {$ifdef HASTHREADVAR}
  88. const
  89. threadvarblocksize : dword = 0;
  90. var
  91. TLSKey : Dword;
  92. procedure SysInitThreadvar(var offset : dword;size : dword);
  93. begin
  94. offset:=threadvarblocksize;
  95. inc(threadvarblocksize,size);
  96. end;
  97. function SysRelocateThreadvar(offset : dword) : pointer;
  98. begin
  99. SysRelocateThreadvar:=TlsGetValue(tlskey)+Offset;
  100. end;
  101. procedure SysAllocateThreadVars;
  102. var
  103. dataindex : pointer;
  104. begin
  105. { we've to allocate the memory from system }
  106. { because the FPC heap management uses }
  107. { exceptions which use threadvars but }
  108. { these aren't allocated yet ... }
  109. { allocate room on the heap for the thread vars }
  110. dataindex:=pointer(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,threadvarblocksize));
  111. TlsSetValue(tlskey,dataindex);
  112. end;
  113. procedure SysReleaseThreadVars;
  114. begin
  115. GlobalFree(TlsGetValue(tlskey));
  116. end;
  117. { Include OS independent Threadvar initialization }
  118. {$i threadvr.inc}
  119. {$endif HASTHREADVAR}
  120. {*****************************************************************************
  121. Thread starting
  122. *****************************************************************************}
  123. type
  124. pthreadinfo = ^tthreadinfo;
  125. tthreadinfo = record
  126. f : tthreadfunc;
  127. p : pointer;
  128. stklen : cardinal;
  129. end;
  130. procedure DoneThread;
  131. begin
  132. { Release Threadvars }
  133. {$ifdef HASTHREADVAR}
  134. SysReleaseThreadVars;
  135. {$endif HASTHREADVAR}
  136. end;
  137. function ThreadMain(param : pointer) : integer; stdcall;
  138. var
  139. ti : tthreadinfo;
  140. begin
  141. {$ifdef HASTHREADVAR}
  142. { Allocate local thread vars, this must be the first thing,
  143. because the exception management and io depends on threadvars }
  144. SysAllocateThreadVars;
  145. {$endif HASTHREADVAR}
  146. { Copy parameter to local data }
  147. {$ifdef DEBUG_MT}
  148. writeln('New thread started, initialising ...');
  149. {$endif DEBUG_MT}
  150. ti:=pthreadinfo(param)^;
  151. dispose(pthreadinfo(param));
  152. { Initialize thread }
  153. InitThread(ti.stklen);
  154. { Start thread function }
  155. {$ifdef DEBUG_MT}
  156. writeln('Jumping to thread function');
  157. {$endif DEBUG_MT}
  158. ThreadMain:=ti.f(ti.p);
  159. end;
  160. function SysBeginThread(sa : Pointer;stacksize : dword;
  161. ThreadFunction : tthreadfunc;p : pointer;
  162. creationFlags : dword; var ThreadId : DWord) : DWord;
  163. var
  164. ti : pthreadinfo;
  165. begin
  166. {$ifdef DEBUG_MT}
  167. writeln('Creating new thread');
  168. {$endif DEBUG_MT}
  169. { Initialize multithreading if not done }
  170. if not IsMultiThread then
  171. begin
  172. {$ifdef HASTHREADVAR}
  173. { We're still running in single thread mode, setup the TLS }
  174. TLSKey:=TlsAlloc;
  175. InitThreadVars(@SysRelocateThreadvar);
  176. {$endif HASTHREADVAR}
  177. IsMultiThread:=true;
  178. end;
  179. { the only way to pass data to the newly created thread
  180. in a MT safe way, is to use the heap }
  181. new(ti);
  182. ti^.f:=ThreadFunction;
  183. ti^.p:=p;
  184. ti^.stklen:=stacksize;
  185. { call pthread_create }
  186. {$ifdef DEBUG_MT}
  187. writeln('Starting new thread');
  188. {$endif DEBUG_MT}
  189. SysBeginThread:=CreateThread(sa,stacksize,@ThreadMain,ti,creationflags,threadid);
  190. end;
  191. procedure SysEndThread(ExitCode : DWord);
  192. begin
  193. DoneThread;
  194. ExitThread(ExitCode);
  195. end;
  196. procedure SysThreadSwitch;
  197. begin
  198. Sleep(0);
  199. end;
  200. function SysSuspendThread (threadHandle : dword) : dword;
  201. begin
  202. SysSuspendThread:=WinSuspendThread(threadHandle);
  203. end;
  204. function SysResumeThread (threadHandle : dword) : dword;
  205. begin
  206. SysResumeThread:=WinResumeThread(threadHandle);
  207. end;
  208. function SysKillThread (threadHandle : dword) : dword;
  209. var exitCode : dword;
  210. begin
  211. if not TerminateThread (threadHandle, exitCode) then
  212. SysKillThread := GetLastError
  213. else
  214. SysKillThread := 0;
  215. end;
  216. function SysWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword;
  217. begin
  218. if timeoutMs = 0 then dec (timeoutMs); // $ffffffff is INFINITE
  219. SysWaitForThreadTerminate := WaitForSingleObject(threadHandle, TimeoutMs);
  220. end;
  221. function SysThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  222. begin
  223. SysThreadSetPriority:=WinThreadSetPriority(threadHandle,Prio);
  224. end;
  225. function SysThreadGetPriority (threadHandle : dword): Integer;
  226. begin
  227. SysThreadGetPriority:=WinThreadGetPriority(threadHandle);
  228. end;
  229. function SysGetCurrentThreadId : dword;
  230. begin
  231. SysGetCurrentThreadId:=WinGetCurrentThreadId;
  232. end;
  233. {*****************************************************************************
  234. Delphi/Win32 compatibility
  235. *****************************************************************************}
  236. procedure WinInitCriticalSection(var cs : TRTLCriticalSection);
  237. stdcall;external 'kernel32' name 'InitializeCriticalSection';
  238. procedure WinDoneCriticalSection(var cs : TRTLCriticalSection);
  239. stdcall;external 'kernel32' name 'DeleteCriticalSection';
  240. procedure WinEnterCriticalSection(var cs : TRTLCriticalSection);
  241. stdcall;external 'kernel32' name 'EnterCriticalSection';
  242. procedure WinLeaveCriticalSection(var cs : TRTLCriticalSection);
  243. stdcall;external 'kernel32' name 'LeaveCriticalSection';
  244. procedure SySInitCriticalSection(var cs);
  245. begin
  246. WinInitCriticalSection(PRTLCriticalSection(@cs)^);
  247. end;
  248. procedure SysDoneCriticalSection(var cs);
  249. begin
  250. WinDoneCriticalSection(PRTLCriticalSection(@cs)^);
  251. end;
  252. procedure SysEnterCriticalSection(var cs);
  253. begin
  254. WinEnterCriticalSection(PRTLCriticalSection(@cs)^);
  255. end;
  256. procedure SySLeaveCriticalSection(var cs);
  257. begin
  258. WinLeaveCriticalSection(PRTLCriticalSection(@cs)^);
  259. end;
  260. {*****************************************************************************
  261. Heap Mutex Protection
  262. *****************************************************************************}
  263. var
  264. HeapMutex : TRTLCriticalSection;
  265. procedure Win32HeapMutexInit;
  266. begin
  267. InitCriticalSection(heapmutex);
  268. end;
  269. procedure Win32HeapMutexDone;
  270. begin
  271. DoneCriticalSection(heapmutex);
  272. end;
  273. procedure Win32HeapMutexLock;
  274. begin
  275. EnterCriticalSection(heapmutex);
  276. end;
  277. procedure Win32HeapMutexUnlock;
  278. begin
  279. LeaveCriticalSection(heapmutex);
  280. end;
  281. const
  282. Win32MemoryMutexManager : TMemoryMutexManager = (
  283. MutexInit : @Win32HeapMutexInit;
  284. MutexDone : @Win32HeapMutexDone;
  285. MutexLock : @Win32HeapMutexLock;
  286. MutexUnlock : @Win32HeapMutexUnlock;
  287. );
  288. procedure InitHeapMutexes;
  289. begin
  290. SetMemoryMutexManager(Win32MemoryMutexManager);
  291. end;
  292. Const
  293. wrSignaled = 0;
  294. wrTimeout = 1;
  295. wrAbandoned= 2;
  296. wrError = 3;
  297. type Tbasiceventstate=record
  298. fhandle : THandle;
  299. flasterror : longint;
  300. end;
  301. plocaleventrec= ^tbasiceventstate;
  302. function intBasicEventCreate(EventAttributes : Pointer;
  303. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  304. begin
  305. new(plocaleventrec(result));
  306. plocaleventrec(result)^.FHandle := CreateEvent(EventAttributes, AManualReset, InitialState,PChar(Name));
  307. end;
  308. procedure intbasiceventdestroy(state:peventstate);
  309. begin
  310. closehandle(plocaleventrec(state)^.fhandle);
  311. dispose(plocaleventrec(state));
  312. end;
  313. procedure intbasiceventResetEvent(state:peventstate);
  314. begin
  315. ResetEvent(plocaleventrec(state)^.FHandle)
  316. end;
  317. procedure intbasiceventSetEvent(state:peventstate);
  318. begin
  319. SetEvent(plocaleventrec(state)^.FHandle);
  320. end;
  321. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  322. begin
  323. case WaitForSingleObject(plocaleventrec(state)^.fHandle, Timeout) of
  324. WAIT_ABANDONED: Result := wrAbandoned;
  325. WAIT_OBJECT_0: Result := wrSignaled;
  326. WAIT_TIMEOUT: Result := wrTimeout;
  327. WAIT_FAILED:
  328. begin
  329. Result := wrError;
  330. plocaleventrec(state)^.FLastError := GetLastError;
  331. end;
  332. else
  333. Result := wrError;
  334. end;
  335. end;
  336. function intRTLEventCreate: PRTLEvent;
  337. begin
  338. Result := PRTLEVENT(CreateEvent(nil, false, false, nil));
  339. end;
  340. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  341. begin
  342. CloseHandle(THANDLE(AEvent));
  343. end;
  344. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  345. begin
  346. PulseEvent(THANDLE(AEvent));
  347. end;
  348. CONST INFINITE=-1;
  349. procedure intRTLEventStartWait(AEvent: PRTLEvent);
  350. begin
  351. // nothing to do, win32 events stay signalled after being set
  352. end;
  353. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  354. begin
  355. WaitForSingleObject(THANDLE(AEvent), INFINITE);
  356. end;
  357. Var
  358. WinThreadManager : TThreadManager;
  359. Procedure SetWinThreadManager;
  360. begin
  361. With WinThreadManager do
  362. begin
  363. InitManager :=Nil;
  364. DoneManager :=Nil;
  365. BeginThread :=@SysBeginThread;
  366. EndThread :=@SysEndThread;
  367. SuspendThread :=@SysSuspendThread;
  368. ResumeThread :=@SysResumeThread;
  369. KillThread :=@SysKillThread;
  370. ThreadSwitch :=@SysThreadSwitch;
  371. WaitForThreadTerminate :=@SysWaitForThreadTerminate;
  372. ThreadSetPriority :=@SysThreadSetPriority;
  373. ThreadGetPriority :=@SysThreadGetPriority;
  374. GetCurrentThreadId :=@SysGetCurrentThreadId;
  375. InitCriticalSection :=@SysInitCriticalSection;
  376. DoneCriticalSection :=@SysDoneCriticalSection;
  377. EnterCriticalSection :=@SysEnterCriticalSection;
  378. LeaveCriticalSection :=@SysLeaveCriticalSection;
  379. {$ifdef HASTHREADVAR}
  380. InitThreadVar :=@SysInitThreadVar;
  381. RelocateThreadVar :=@SysRelocateThreadVar;
  382. AllocateThreadVars :=@SysAllocateThreadVars;
  383. ReleaseThreadVars :=@SysReleaseThreadVars;
  384. {$endif HASTHREADVAR}
  385. BasicEventCreate :=@intBasicEventCreate;
  386. BasicEventDestroy :=@intBasicEventDestroy;
  387. BasicEventResetEvent :=@intBasicEventResetEvent;
  388. BasicEventSetEvent :=@intBasicEventSetEvent;
  389. BasiceventWaitFor :=@intBasiceventWaitFor;
  390. RTLEventCreate :=@intRTLEventCreate;
  391. RTLEventDestroy :=@intRTLEventDestroy;
  392. RTLEventSetEvent :=@intRTLEventSetEvent;
  393. RTLEventStartWait :=@intRTLEventStartWait;
  394. RTLEventWaitFor :=@intRTLEventWaitFor;
  395. end;
  396. SetThreadManager(WinThreadManager);
  397. InitHeapMutexes;
  398. end;
  399. initialization
  400. SetWinThreadManager;
  401. end.
  402. {
  403. $Log$
  404. Revision 1.15 2005-01-30 21:48:14 marco
  405. * stdcall added to few calls
  406. Revision 1.14 2004/12/28 14:20:03 marco
  407. * tthread patch from neli
  408. Revision 1.13 2004/12/26 13:46:45 peter
  409. * tthread uses systhrds
  410. Revision 1.12 2004/12/22 21:29:24 marco
  411. * rtlevent kraam. Checked (compile): Linux, FreeBSD, Darwin, Windows
  412. Check work: ask Neli.
  413. Revision 1.11 2004/05/23 15:30:13 marco
  414. * first try
  415. Revision 1.10 2004/01/21 14:15:42 florian
  416. * fixed win32 compilation
  417. Revision 1.9 2003/11/29 17:34:53 michael
  418. + Removed dummy variable from SetCthreadManager
  419. Revision 1.8 2003/11/27 10:28:41 michael
  420. + Patch from peter to fix make cycle
  421. Revision 1.7 2003/11/26 20:10:59 michael
  422. + New threadmanager implementation
  423. Revision 1.6 2003/10/01 21:00:09 peter
  424. * GetCurrentThreadHandle renamed to GetCurrentThreadId
  425. Revision 1.5 2003/09/17 15:06:36 peter
  426. * stdcall patch
  427. Revision 1.4 2003/03/27 17:14:27 armin
  428. * more platform independent thread routines, needs to be implemented for unix
  429. Revision 1.3 2003/03/24 16:12:01 jonas
  430. * BeginThread() now returns the thread handle instead of the threadid
  431. (needed because you have to free the handle after your thread is
  432. finished, and the threadid is already returned via a var-parameter)
  433. Revision 1.2 2002/10/31 13:45:44 carl
  434. * threadvar.inc -> threadvr.inc
  435. Revision 1.1 2002/10/16 06:27:30 michael
  436. + Renamed thread unit to systhrds
  437. Revision 1.1 2002/10/14 19:39:18 peter
  438. * threads unit added for thread support
  439. }