systhrds.pp 15 KB

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