systhrd.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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. Linux (pthreads) 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. { Multithreading for netware, armin 16 Mar 2002
  14. - threads are basicly tested and working
  15. - TRTLCriticalSections are working but NEVER call Enter or
  16. LeaveCriticalSection with uninitialized CriticalSections.
  17. Critial Sections are based on local semaphores and the
  18. Server will abend if the semaphore handles are invalid. There
  19. are basic tests in the rtl but this will not work in every case.
  20. Not closed semaphores will be closed by the rtl on program
  21. termination because some versions of netware will abend if there
  22. are open semaphores on nlm unload.
  23. }
  24. {*****************************************************************************
  25. Threadvar support
  26. *****************************************************************************}
  27. {$ifdef HASTHREADVAR}
  28. const
  29. threadvarblocksize : dword = 0; // total size of allocated threadvars
  30. thredvarsmainthread: pointer = nil; // to free the threadvars in the signal handler
  31. procedure SysInitThreadvar (var offset : dword;size : dword);[public,alias: 'FPC_INIT_THREADVAR'];
  32. begin
  33. offset:=threadvarblocksize;
  34. inc(threadvarblocksize,size);
  35. {$ifdef DEBUG_MT}
  36. ConsolePrintf(#13'init_threadvar, new offset: (%d), Size:%d'#13#10,offset,size,0);
  37. {$endif DEBUG_MT}
  38. end;
  39. {$ifdef DEBUG_MT}
  40. var dummy_buff : array [0..255] of char; // to avoid abends (for current compiler error that not all threadvars are initialized)
  41. {$endif}
  42. function SysRelocateThreadvar (offset : dword) : pointer;
  43. var p : pointer;
  44. begin
  45. {$ifdef DEBUG_MT}
  46. // ConsolePrintf(#13'relocate_threadvar, offset: (%d)'#13#10,offset);
  47. if offset > threadvarblocksize then
  48. begin
  49. // ConsolePrintf(#13'relocate_threadvar, invalid offset'#13#10,0);
  50. SysRelocateThreadvar := @dummy_buff;
  51. exit;
  52. end;
  53. {$endif DEBUG_MT}
  54. SysRelocateThreadvar:= _GetThreadDataAreaPtr + offset;
  55. end;
  56. procedure SysAllocateThreadVars;
  57. var
  58. threadvars : pointer;
  59. begin
  60. { we've to allocate the memory from netware }
  61. { because the FPC heap management uses }
  62. { exceptions which use threadvars but }
  63. { these aren't allocated yet ... }
  64. { allocate room on the heap for the thread vars }
  65. threadvars := _malloc (threadvarblocksize);
  66. fillchar (threadvars^, threadvarblocksize, 0);
  67. _SaveThreadDataAreaPtr (threadvars);
  68. {$ifdef DEBUG_MT}
  69. ConsolePrintf(#13'threadvars allocated at (%x), size: %d'#13#10,longint(threadvars),threadvarblocksize,0);
  70. {$endif DEBUG_MT}
  71. if thredvarsmainthread = nil then
  72. thredvarsmainthread := threadvars;
  73. end;
  74. procedure SysReleaseThreadVars;
  75. var threadvars : pointer;
  76. begin
  77. { release thread vars }
  78. if threadvarblocksize > 0 then
  79. begin
  80. threadvars:=_GetThreadDataAreaPtr;
  81. if threadvars <> nil then
  82. begin
  83. {$ifdef DEBUG_MT}
  84. ConsolePrintf (#13'free threadvars'#13#10,0);
  85. {$endif DEBUG_MT}
  86. _Free (threadvars);
  87. _SaveThreadDataAreaPtr (nil);
  88. end;
  89. end;
  90. end;
  91. {$endif HASTHREADVAR}
  92. {*****************************************************************************
  93. Thread starting
  94. *****************************************************************************}
  95. type
  96. tthreadinfo = record
  97. f : tthreadfunc;
  98. p : pointer;
  99. stklen: cardinal;
  100. end;
  101. pthreadinfo = ^tthreadinfo;
  102. procedure DoneThread;
  103. begin
  104. { release thread vars }
  105. {$ifdef HASTHREADVAR}
  106. SysReleaseThreadVars;
  107. {$endif}
  108. end;
  109. function ThreadMain(param : pointer) : dword; cdecl;
  110. var
  111. ti : tthreadinfo;
  112. begin
  113. {$ifdef HASTHREADVAR}
  114. { Allocate local thread vars, this must be the first thing,
  115. because the exception management and io depends on threadvars }
  116. SysAllocateThreadVars;
  117. {$endif HASTHREADVAR}
  118. {$ifdef DEBUG_MT}
  119. ConsolePrintf(#13'New thread %x started, initialising ...'#13#10,_GetThreadID);
  120. {$endif DEBUG_MT}
  121. ti:=pthreadinfo(param)^;
  122. InitThread(ti.stklen);
  123. dispose(pthreadinfo(param));
  124. {$ifdef DEBUG_MT}
  125. ConsolePrintf(#13'Jumping to thread function'#13#10);
  126. {$endif DEBUG_MT}
  127. ThreadMain:=ti.f(ti.p);
  128. DoneThread;
  129. end;
  130. function SysBeginThread(sa : Pointer;stacksize : dword;
  131. ThreadFunction : tthreadfunc;p : pointer;
  132. creationFlags : dword; var ThreadId : DWord) : DWord;
  133. var ti : pthreadinfo;
  134. begin
  135. {$ifdef DEBUG_MT}
  136. ConsolePrintf(#13'Creating new thread'#13#10);
  137. {$endif DEBUG_MT}
  138. {$ifdef HASTHREADVAR}
  139. if not IsMultiThread then
  140. begin
  141. InitThreadVars(@SysRelocateThreadvar);
  142. IsMultithread:=true;
  143. end;
  144. {$endif}
  145. { the only way to pass data to the newly created thread }
  146. { in a MT safe way, is to use the heap }
  147. new(ti);
  148. ti^.f:=ThreadFunction;
  149. ti^.p:=p;
  150. ti^.stklen:=stacksize;
  151. {$ifdef DEBUG_MT}
  152. ConsolePrintf(#13'Starting new thread'#13#10);
  153. {$endif DEBUG_MT}
  154. SysBeginThread :=
  155. _BeginThread (@ThreadMain,NIL,Stacksize,ti);
  156. end;
  157. procedure SysEndThread(ExitCode : DWord);
  158. begin
  159. {$ifdef DEBUG_MT}
  160. ConsolePrintf (#13'SysEndThread %x'#13#10,_GetThreadID);
  161. {$endif}
  162. DoneThread;
  163. ExitThread(ExitCode , TSR_THREAD);
  164. end;
  165. {*****************************************************************************
  166. Thread handling
  167. *****************************************************************************}
  168. function __SuspendThread (threadId : dword) : dword; cdecl; external 'clib' name 'SuspendThread';
  169. function __ResumeThread (threadId : dword) : dword; cdecl; external 'clib' name 'ResumeThread';
  170. procedure __ThreadSwitchWithDelay; cdecl; external 'clib' name 'ThreadSwitchWithDelay';
  171. procedure SysThreadSwitch;
  172. begin
  173. __ThreadSwitchWithDelay;
  174. end;
  175. {redefined because the interface has not cdecl calling convention}
  176. function SysSuspendThread (threadHandle : dword) : dword;
  177. begin
  178. SysSuspendThread := __SuspendThread (threadHandle);
  179. end;
  180. function SysResumeThread (threadHandle : dword) : dword;
  181. begin
  182. SysResumeThread := __ResumeThread (threadHandle);
  183. end;
  184. function SysKillThread (threadHandle : dword) : dword;
  185. begin
  186. SysKillThread := 1; {not supported for netware}
  187. end;
  188. function GetThreadName (threadId : longint; var threadName) : longint; cdecl; external 'clib' name 'GetThreadName';
  189. function CGetThreadID : dword; cdecl; external 'clib' name 'GetThreadID';
  190. //function __RenameThread (threadId : longint; threadName:pchar) : longint; cdecl; external 'clib' name 'RenameThread';
  191. function SysWaitForThreadTerminate (threadHandle : dword; TimeoutMs : longint) : dword;
  192. var
  193. status : longint;
  194. buf : array [0..50] of char;
  195. begin
  196. {$warning timeout needs to be implemented}
  197. {$ifdef DEBUG_MT}
  198. ConsolePrintf (#13'SysWaitForThreadTerminate ThreadID:%x Handle:%x'#13#10,GetThreadID,threadHandle);
  199. {$endif}
  200. repeat
  201. status := GetThreadName (ThreadHandle,Buf); {should return EBADHNDL if thread is terminated}
  202. ThreadSwitch;
  203. until status <> 0;
  204. SysWaitForThreadTerminate:=0;
  205. end;
  206. function SysThreadSetPriority (threadHandle : dword; Prio: longint): boolean; {-15..+15, 0=normal}
  207. begin
  208. SysThreadSetPriority := true;
  209. end;
  210. function SysThreadGetPriority (threadHandle : dword): Longint;
  211. begin
  212. SysThreadGetPriority := 0;
  213. end;
  214. function SysGetCurrentThreadId : dword;
  215. begin
  216. SysGetCurrentThreadId := CGetThreadID;
  217. end;
  218. { netware requires all allocated semaphores }
  219. { to be closed before terminating the nlm, otherwise }
  220. { the server will abend (except for netware 6 i think) }
  221. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  222. PSemaList = ^TSemaList;
  223. CONST NumSemaOpen : LONGINT = 0;
  224. NumEntriesMax : LONGINT = 0;
  225. SemaList : PSemaList = NIL;
  226. PROCEDURE SaveSema (Handle : LONGINT);
  227. BEGIN
  228. {$ifdef DEBUG_MT}
  229. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  230. {$endif DEBUG_MT}
  231. _EnterCritSec;
  232. IF NumSemaOpen = NumEntriesMax THEN
  233. BEGIN
  234. IF SemaList = NIL THEN
  235. BEGIN
  236. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  237. NumEntriesMax := 32;
  238. END ELSE
  239. BEGIN
  240. INC (NumEntriesMax, 16);
  241. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  242. END;
  243. END;
  244. INC (NumSemaOpen);
  245. SemaList^[NumSemaOpen] := Handle;
  246. _ExitCritSec;
  247. END;
  248. PROCEDURE ReleaseSema (Handle : LONGINT);
  249. VAR I : LONGINT;
  250. BEGIN
  251. {$ifdef DEBUG_MT}
  252. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  253. {$endif DEBUG_MT}
  254. _EnterCritSec;
  255. IF SemaList <> NIL then
  256. if NumSemaOpen > 0 then
  257. begin
  258. for i := 1 to NumSemaOpen do
  259. if SemaList^[i] = Handle then
  260. begin
  261. if i < NumSemaOpen then
  262. SemaList^[i] := SemaList^[NumSemaOpen];
  263. dec (NumSemaOpen);
  264. _ExitCritSec;
  265. exit;
  266. end;
  267. end;
  268. _ExitCritSec;
  269. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  270. END;
  271. PROCEDURE SysCloseAllRemainingSemaphores;
  272. var i : LONGINT;
  273. begin
  274. IF SemaList <> NIL then
  275. begin
  276. if NumSemaOpen > 0 then
  277. for i := 1 to NumSemaOpen do
  278. _CloseLocalSemaphore (SemaList^[i]);
  279. _free (SemaList);
  280. SemaList := NIL;
  281. NumSemaOpen := 0;
  282. NumEntriesMax := 0;
  283. end;
  284. end;
  285. { this allows to do a lot of things in MT safe way }
  286. { it is also used to make the heap management }
  287. { thread safe }
  288. procedure SysInitCriticalSection(var cs);// : TRTLCriticalSection);
  289. begin
  290. with PRTLCriticalSection(@cs)^ do
  291. begin
  292. SemaHandle := _OpenLocalSemaphore (1);
  293. if SemaHandle <> 0 then
  294. begin
  295. SemaIsOpen := true;
  296. SaveSema (SemaHandle);
  297. end else
  298. begin
  299. SemaIsOpen := false;
  300. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  301. end;
  302. end;
  303. end;
  304. procedure SysDoneCriticalsection(var cs);
  305. begin
  306. with PRTLCriticalSection(@cs)^ do
  307. begin
  308. if SemaIsOpen then
  309. begin
  310. _CloseLocalSemaphore (SemaHandle);
  311. ReleaseSema (SemaHandle);
  312. SemaIsOpen := FALSE;
  313. end;
  314. end;
  315. end;
  316. procedure SysEnterCriticalsection(var cs);
  317. begin
  318. with PRTLCriticalSection(@cs)^ do
  319. begin
  320. if SemaIsOpen then
  321. _WaitOnLocalSemaphore (SemaHandle)
  322. else
  323. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  324. end;
  325. end;
  326. procedure SysLeaveCriticalSection(var cs);
  327. begin
  328. with PRTLCriticalSection(@cs)^ do
  329. begin
  330. if SemaIsOpen then
  331. _SignalLocalSemaphore (SemaHandle)
  332. else
  333. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  334. end;
  335. end;
  336. function SysSetThreadDataAreaPtr (newPtr:pointer):pointer;
  337. begin
  338. SysSetThreadDataAreaPtr := _GetThreadDataAreaPtr;
  339. if newPtr = nil then
  340. newPtr := thredvarsmainthread;
  341. _SaveThreadDataAreaPtr (newPtr);
  342. end;
  343. {*****************************************************************************
  344. Heap Mutex Protection
  345. *****************************************************************************}
  346. var
  347. HeapMutex : TRTLCriticalSection;
  348. procedure NWHeapMutexInit;
  349. begin
  350. InitCriticalSection(heapmutex);
  351. end;
  352. procedure NWHeapMutexDone;
  353. begin
  354. DoneCriticalSection(heapmutex);
  355. end;
  356. procedure NWHeapMutexLock;
  357. begin
  358. EnterCriticalSection(heapmutex);
  359. end;
  360. procedure NWHeapMutexUnlock;
  361. begin
  362. LeaveCriticalSection(heapmutex);
  363. end;
  364. const
  365. NWMemoryMutexManager : TMemoryMutexManager = (
  366. MutexInit : @NWHeapMutexInit;
  367. MutexDone : @NWHeapMutexDone;
  368. MutexLock : @NWHeapMutexLock;
  369. MutexUnlock : @NWHeapMutexUnlock;
  370. );
  371. procedure InitHeapMutexes;
  372. begin
  373. SetMemoryMutexManager(NWMemoryMutexManager);
  374. end;
  375. Var
  376. NWThreadManager : TThreadManager;
  377. Procedure InitSystemThreads;
  378. begin
  379. With NWThreadManager do
  380. begin
  381. InitManager :=Nil;
  382. DoneManager :=Nil;
  383. BeginThread :=@SysBeginThread;
  384. EndThread :=@SysEndThread;
  385. SuspendThread :=@SysSuspendThread;
  386. ResumeThread :=@SysResumeThread;
  387. KillThread :=@SysKillThread;
  388. ThreadSwitch :=@SysThreadSwitch;
  389. WaitForThreadTerminate :=@SysWaitForThreadTerminate;
  390. ThreadSetPriority :=@SysThreadSetPriority;
  391. ThreadGetPriority :=@SysThreadGetPriority;
  392. GetCurrentThreadId :=@SysGetCurrentThreadId;
  393. InitCriticalSection :=@SysInitCriticalSection;
  394. DoneCriticalSection :=@SysDoneCriticalSection;
  395. EnterCriticalSection :=@SysEnterCriticalSection;
  396. LeaveCriticalSection :=@SysLeaveCriticalSection;
  397. {$ifdef HASTHREADVAR}
  398. InitThreadVar :=@SysInitThreadVar;
  399. RelocateThreadVar :=@SysRelocateThreadVar;
  400. AllocateThreadVars :=@SysAllocateThreadVars;
  401. ReleaseThreadVars :=@SysReleaseThreadVars;
  402. {$endif HASTHREADVAR}
  403. BasicEventCreate :=@NoBasicEventCreate;
  404. basiceventdestroy :=@Nobasiceventdestroy;
  405. basiceventResetEvent :=@NobasiceventResetEvent;
  406. basiceventSetEvent :=@NobasiceventSetEvent;
  407. basiceventWaitFor :=@NobasiceventWaitFor;
  408. end;
  409. SetThreadManager(NWThreadManager);
  410. InitHeapMutexes;
  411. NWSysSetThreadFunctions (@SysCloseAllRemainingSemaphores,
  412. @SysReleaseThreadVars,
  413. @SysSetThreadDataAreaPtr);
  414. end;
  415. {
  416. $Log$
  417. Revision 1.2 2005-02-14 17:13:30 peter
  418. * truncate log
  419. Revision 1.1 2005/02/06 16:57:18 peter
  420. * threads for go32v2,os,emx,netware
  421. Revision 1.1 2005/02/06 13:06:20 peter
  422. * moved file and dir functions to sysfile/sysdir
  423. * win32 thread in systemunit
  424. }