systhrd.inc 13 KB

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