systhrds.pp 12 KB

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