systhrds.pp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. implementation
  29. {$i thread.inc }
  30. { some declarations for Netware API calls }
  31. {$I nwsys.inc}
  32. { define DEBUG_MT}
  33. const
  34. threadvarblocksize : dword = 0; // total size of allocated threadvars
  35. thredvarsmainthread: pointer = nil; // to free the threadvars in the signal handler
  36. procedure SysInitThreadvar (var offset : dword;size : dword);[public,alias: 'FPC_INIT_THREADVAR'];
  37. begin
  38. offset:=threadvarblocksize;
  39. inc(threadvarblocksize,size);
  40. {$ifdef DEBUG_MT}
  41. ConsolePrintf3(#13'init_threadvar, new offset: (%d), Size:%d'#13#10,offset,size,0);
  42. {$endif DEBUG_MT}
  43. end;
  44. {$ifdef DEBUG_MT}
  45. var dummy_buff : array [0..255] of char; // to avoid abends (for current compiler error that not all threadvars are initialized)
  46. {$endif}
  47. function SysRelocateThreadvar (offset : dword) : pointer;
  48. var p : pointer;
  49. begin
  50. {$ifdef DEBUG_MT}
  51. // ConsolePrintf(#13'relocate_threadvar, offset: (%d)'#13#10,offset);
  52. if offset > threadvarblocksize then
  53. begin
  54. // ConsolePrintf(#13'relocate_threadvar, invalid offset'#13#10,0);
  55. SysRelocateThreadvar := @dummy_buff;
  56. exit;
  57. end;
  58. {$endif DEBUG_MT}
  59. SysRelocateThreadvar:= _GetThreadDataAreaPtr + offset;
  60. end;
  61. procedure SysAllocateThreadVars;
  62. var
  63. threadvars : pointer;
  64. begin
  65. { we've to allocate the memory from netware }
  66. { because the FPC heap management uses }
  67. { exceptions which use threadvars but }
  68. { these aren't allocated yet ... }
  69. { allocate room on the heap for the thread vars }
  70. threadvars := _malloc (threadvarblocksize);
  71. fillchar (threadvars^, threadvarblocksize, 0);
  72. _SaveThreadDataAreaPtr (threadvars);
  73. {$ifdef DEBUG_MT}
  74. ConsolePrintf3(#13'threadvars allocated at (%x), size: %d'#13#10,longint(threadvars),threadvarblocksize,0);
  75. {$endif DEBUG_MT}
  76. if thredvarsmainthread = nil then
  77. thredvarsmainthread := threadvars;
  78. end;
  79. procedure SysReleaseThreadVars;
  80. var threadvars : pointer;
  81. begin
  82. { release thread vars }
  83. if threadvarblocksize > 0 then
  84. begin
  85. threadvars:=_GetThreadDataAreaPtr;
  86. if threadvars <> nil then
  87. begin
  88. {$ifdef DEBUG_MT}
  89. ConsolePrintf (#13'free threadvars'#13#10,0);
  90. {$endif DEBUG_MT}
  91. _Free (threadvars);
  92. _SaveThreadDataAreaPtr (nil);
  93. end;
  94. end;
  95. end;
  96. { Include OS independent Threadvar initialization }
  97. {$i threadvr.inc}
  98. {*****************************************************************************
  99. Thread starting
  100. *****************************************************************************}
  101. type
  102. tthreadinfo = record
  103. f : tthreadfunc;
  104. p : pointer;
  105. stklen: cardinal;
  106. end;
  107. pthreadinfo = ^tthreadinfo;
  108. procedure DoneThread;
  109. begin
  110. { release thread vars }
  111. SysReleaseThreadVars;
  112. end;
  113. function ThreadMain(param : pointer) : dword; cdecl;
  114. var
  115. ti : tthreadinfo;
  116. begin
  117. SysAllocateThreadVars;
  118. {$ifdef DEBUG_MT}
  119. ConsolePrintf(#13'New thread started, initialising ...'#13#10);
  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 BeginThread(sa : Pointer;stacksize : dword;
  131. ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword;
  132. 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. if not IsMultiThread then
  139. begin
  140. InitThreadVars(@SysRelocateThreadvar);
  141. IsMultithread:=true;
  142. end;
  143. { the only way to pass data to the newly created thread }
  144. { in a MT safe way, is to use the heap }
  145. new(ti);
  146. ti^.f:=ThreadFunction;
  147. ti^.p:=p;
  148. ti^.stklen:=stacksize;
  149. {$ifdef DEBUG_MT}
  150. ConsolePrintf(#13'Starting new thread'#13#10);
  151. {$endif DEBUG_MT}
  152. BeginThread :=
  153. _BeginThread (@ThreadMain,NIL,Stacksize,ti);
  154. end;
  155. procedure EndThread(ExitCode : DWord);
  156. begin
  157. DoneThread;
  158. ExitThread(ExitCode , TSR_THREAD);
  159. end;
  160. { netware requires all allocated semaphores }
  161. { to be closed before terminating the nlm, otherwise }
  162. { the server will abend (except for netware 6 i think) }
  163. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  164. PSemaList = ^TSemaList;
  165. CONST NumSemaOpen : LONGINT = 0;
  166. NumEntriesMax : LONGINT = 0;
  167. SemaList : PSemaList = NIL;
  168. PROCEDURE SaveSema (Handle : LONGINT);
  169. BEGIN
  170. {$ifdef DEBUG_MT}
  171. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  172. {$endif DEBUG_MT}
  173. _EnterCritSec;
  174. IF NumSemaOpen = NumEntriesMax THEN
  175. BEGIN
  176. IF SemaList = NIL THEN
  177. BEGIN
  178. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  179. NumEntriesMax := 32;
  180. END ELSE
  181. BEGIN
  182. INC (NumEntriesMax, 16);
  183. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  184. END;
  185. END;
  186. INC (NumSemaOpen);
  187. SemaList^[NumSemaOpen] := Handle;
  188. _ExitCritSec;
  189. END;
  190. PROCEDURE ReleaseSema (Handle : LONGINT);
  191. VAR I : LONGINT;
  192. BEGIN
  193. {$ifdef DEBUG_MT}
  194. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  195. {$endif DEBUG_MT}
  196. _EnterCritSec;
  197. IF SemaList <> NIL then
  198. if NumSemaOpen > 0 then
  199. begin
  200. for i := 1 to NumSemaOpen do
  201. if SemaList^[i] = Handle then
  202. begin
  203. if i < NumSemaOpen then
  204. SemaList^[i] := SemaList^[NumSemaOpen];
  205. dec (NumSemaOpen);
  206. _ExitCritSec;
  207. exit;
  208. end;
  209. end;
  210. _ExitCritSec;
  211. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  212. END;
  213. PROCEDURE CloseAllRemainingSemaphores;
  214. var i : LONGINT;
  215. begin
  216. IF SemaList <> NIL then
  217. begin
  218. if NumSemaOpen > 0 then
  219. for i := 1 to NumSemaOpen do
  220. _CloseLocalSemaphore (SemaList^[i]);
  221. _free (SemaList);
  222. SemaList := NIL;
  223. NumSemaOpen := 0;
  224. NumEntriesMax := 0;
  225. end;
  226. end;
  227. { this allows to do a lot of things in MT safe way }
  228. { it is also used to make the heap management }
  229. { thread safe }
  230. procedure InitCriticalSection(var cs : TRTLCriticalSection);
  231. begin
  232. cs.SemaHandle := _OpenLocalSemaphore (1);
  233. if cs.SemaHandle <> 0 then
  234. begin
  235. cs.SemaIsOpen := true;
  236. SaveSema (cs.SemaHandle);
  237. end else
  238. begin
  239. cs.SemaIsOpen := false;
  240. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  241. end;
  242. end;
  243. procedure DoneCriticalsection(var cs : TRTLCriticalSection);
  244. begin
  245. if cs.SemaIsOpen then
  246. begin
  247. _CloseLocalSemaphore (cs.SemaHandle);
  248. ReleaseSema (cs.SemaHandle);
  249. cs.SemaIsOpen := FALSE;
  250. end;
  251. end;
  252. procedure EnterCriticalsection(var cs : TRTLCriticalSection);
  253. begin
  254. if cs.SemaIsOpen then
  255. _WaitOnLocalSemaphore (cs.SemaHandle)
  256. else
  257. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  258. end;
  259. procedure LeaveCriticalsection(var cs : TRTLCriticalSection);
  260. begin
  261. if cs.SemaIsOpen then
  262. _SignalLocalSemaphore (cs.SemaHandle)
  263. else
  264. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  265. end;
  266. function SetThreadDataAreaPtr (newPtr:pointer):pointer;
  267. begin
  268. SetThreadDataAreaPtr := _GetThreadDataAreaPtr;
  269. if newPtr = nil then
  270. newPtr := thredvarsmainthread;
  271. _SaveThreadDataAreaPtr (newPtr);
  272. end;
  273. {*****************************************************************************
  274. Heap Mutex Protection
  275. *****************************************************************************}
  276. var
  277. HeapMutex : TRTLCriticalSection;
  278. procedure NWHeapMutexInit;
  279. begin
  280. InitCriticalSection(heapmutex);
  281. end;
  282. procedure NWHeapMutexDone;
  283. begin
  284. DoneCriticalSection(heapmutex);
  285. end;
  286. procedure NWHeapMutexLock;
  287. begin
  288. EnterCriticalSection(heapmutex);
  289. end;
  290. procedure NWHeapMutexUnlock;
  291. begin
  292. LeaveCriticalSection(heapmutex);
  293. end;
  294. const
  295. NWMemoryMutexManager : TMemoryMutexManager = (
  296. MutexInit : @NWHeapMutexInit;
  297. MutexDone : @NWHeapMutexDone;
  298. MutexLock : @NWHeapMutexLock;
  299. MutexUnlock : @NWHeapMutexUnlock;
  300. );
  301. procedure InitHeapMutexes;
  302. begin
  303. SetMemoryMutexManager(NWMemoryMutexManager);
  304. end;
  305. initialization
  306. InitHeapMutexes;
  307. NWSysSetThreadFunctions (@CloseAllRemainingSemaphores,
  308. @SysReleaseThreadVars,
  309. @SetThreadDataAreaPtr);
  310. end.
  311. {
  312. $Log$
  313. Revision 1.1 2003-02-16 17:12:15 armin
  314. * systhrds fir netware added
  315. }