thread.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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. {$ifdef MT}
  13. { Multithreading for netware, armin 16 Mar 2002
  14. - threads are basicly tested and working
  15. - threadvars should work but currently there is a bug in the
  16. compiler preventing using multithreading
  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. const
  27. threadvarblocksize : dword = 0; // total size of allocated threadvars
  28. thredvarsmainthread: pointer = nil; // to free the threadvars in the signal handler
  29. type
  30. tthreadinfo = record
  31. f : tthreadfunc;
  32. p : pointer;
  33. end;
  34. pthreadinfo = ^tthreadinfo;
  35. { all needed import stuff is in nwsys.inc and already included by
  36. system.pp }
  37. procedure init_threadvar(var offset : dword;size : dword);[public,alias: 'FPC_INIT_THREADVAR'];
  38. begin
  39. offset:=threadvarblocksize;
  40. inc(threadvarblocksize,size);
  41. {$ifdef DEBUG_MT}
  42. ConsolePrintf3(#13'init_threadvar, new offset: (%d), Size:%d'#13#10,offset,size,0);
  43. {$endif DEBUG_MT}
  44. end;
  45. type ltvInitEntry =
  46. record
  47. varaddr : pdword;
  48. size : longint;
  49. end;
  50. pltvInitEntry = ^ltvInitEntry;
  51. procedure init_unit_threadvars (tableEntry : pltvInitEntry);
  52. begin
  53. while tableEntry^.varaddr <> nil do
  54. begin
  55. init_threadvar (tableEntry^.varaddr^, tableEntry^.size);
  56. inc (pchar (tableEntry), sizeof (tableEntry^));
  57. end;
  58. end;
  59. type TltvInitTablesTable =
  60. record
  61. count : dword;
  62. tables: array [1..32767] of pltvInitEntry;
  63. end;
  64. var
  65. ThreadvarTablesTable : TltvInitTablesTable; external name 'FPC_LOCALTHREADVARTABLES';
  66. procedure init_all_unit_threadvars; [public,alias: 'FPC_INITIALIZELOCALTHREADVARS'];
  67. var i : integer;
  68. begin
  69. {$ifdef DEBUG_MT}
  70. ConsolePrintf(#13'init_all_unit_threadvars (%d) units'#13#10,ThreadvarTablesTable.count);
  71. {$endif}
  72. for i := 1 to ThreadvarTablesTable.count do
  73. begin
  74. {$ifdef DEBUG_MT}
  75. ConsolePrintf(#13'init_unit_threadvars for unit (%d):'#13#10,i);
  76. {$endif}
  77. init_unit_threadvars (ThreadvarTablesTable.tables[i]);
  78. {$ifdef DEBUG_MT}
  79. ConsolePrintf(#13'init_unit_threadvars for unit (%d) done'#13#10,i);
  80. {$endif}
  81. end;
  82. end;
  83. {$ifdef DEBUG_MT}
  84. var dummy_buff : array [0..255] of char; // to avoid abends (for current compiler error that not all threadvars are initialized)
  85. {$endif}
  86. function relocate_threadvar(offset : dword) : pointer;[public,alias: 'FPC_RELOCATE_THREADVAR'];
  87. var p : pointer;
  88. begin
  89. {$ifdef DEBUG_MT}
  90. // ConsolePrintf(#13'relocate_threadvar, offset: (%d)'#13#10,offset);
  91. if offset > threadvarblocksize then
  92. begin
  93. // ConsolePrintf(#13'relocate_threadvar, invalid offset'#13#10,0);
  94. relocate_threadvar := @dummy_buff;
  95. exit;
  96. end;
  97. {$endif DEBUG_MT}
  98. relocate_threadvar:= _GetThreadDataAreaPtr + offset;
  99. end;
  100. procedure AllocateThreadVars;
  101. var
  102. threadvars : pointer;
  103. begin
  104. { we've to allocate the memory from netware }
  105. { because the FPC heap management uses }
  106. { exceptions which use threadvars but }
  107. { these aren't allocated yet ... }
  108. { allocate room on the heap for the thread vars }
  109. threadvars := _malloc (threadvarblocksize);
  110. fillchar (threadvars^, threadvarblocksize, 0);
  111. _SaveThreadDataAreaPtr (threadvars);
  112. {$ifdef DEBUG_MT}
  113. ConsolePrintf3(#13'threadvars allocated at (%x), size: %d'#13#10,longint(threadvars),threadvarblocksize,0);
  114. {$endif DEBUG_MT}
  115. if thredvarsmainthread = nil then
  116. thredvarsmainthread := threadvars;
  117. end;
  118. procedure ReleaseThreadVars;
  119. var threadvars : pointer;
  120. begin
  121. { release thread vars }
  122. if threadvarblocksize > 0 then
  123. begin
  124. threadvars:=_GetThreadDataAreaPtr;
  125. if threadvars <> nil then
  126. begin
  127. {$ifdef DEBUG_MT}
  128. ConsolePrintf (#13'free threadvars'#13#10,0);
  129. {$endif DEBUG_MT}
  130. _Free (threadvars);
  131. end;
  132. end;
  133. end;
  134. procedure InitThread;
  135. begin
  136. InitFPU;
  137. { we don't need to set the data to 0 because we did this with }
  138. { the fillchar above, but it looks nicer }
  139. { ExceptAddrStack and ExceptObjectStack are threadvars }
  140. { so every thread has its on exception handling capabilities }
  141. InitExceptions;
  142. InOutRes:=0;
  143. // ErrNo:=0;
  144. end;
  145. procedure DoneThread;
  146. begin
  147. { release thread vars }
  148. ReleaseThreadVars;
  149. end;
  150. function ThreadMain(param : pointer) : dword; cdecl;
  151. var
  152. ti : tthreadinfo;
  153. begin
  154. {$ifdef DEBUG_MT}
  155. writeln('New thread started, initialising ...');
  156. {$endif DEBUG_MT}
  157. AllocateThreadVars;
  158. InitThread;
  159. ti:=pthreadinfo(param)^;
  160. dispose(pthreadinfo(param));
  161. {$ifdef DEBUG_MT}
  162. writeln('Jumping to thread function');
  163. {$endif DEBUG_MT}
  164. ThreadMain:=ti.f(ti.p);
  165. DoneThread;
  166. end;
  167. function BeginThread(sa : Pointer;stacksize : dword;
  168. ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword;
  169. var ThreadId : DWord) : DWord;
  170. var ti : pthreadinfo;
  171. begin
  172. {$ifdef DEBUG_MT}
  173. writeln('Creating new thread');
  174. {$endif DEBUG_MT}
  175. IsMultithread:=true;
  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. {$ifdef DEBUG_MT}
  182. writeln('Starting new thread');
  183. {$endif DEBUG_MT}
  184. BeginThread :=
  185. _BeginThread (@ThreadMain,NIL,Stacksize,ti);
  186. end;
  187. function BeginThread(ThreadFunction : tthreadfunc) : DWord;
  188. var dummy : dword;
  189. begin
  190. BeginThread:=BeginThread(nil,0,ThreadFunction,nil,0,dummy);
  191. end;
  192. function BeginThread(ThreadFunction : tthreadfunc;p : pointer) : DWord;
  193. var dummy : dword;
  194. begin
  195. BeginThread:=BeginThread(nil,0,ThreadFunction,p,0,dummy);
  196. end;
  197. function BeginThread(ThreadFunction : tthreadfunc;p : pointer;var ThreadId : DWord) : DWord;
  198. begin
  199. BeginThread:=BeginThread(nil,0,ThreadFunction,p,0,ThreadId);
  200. end;
  201. procedure EndThread(ExitCode : DWord);
  202. begin
  203. DoneThread;
  204. ExitThread(ExitCode, TSR_THREAD);
  205. end;
  206. procedure EndThread;
  207. begin
  208. EndThread(0);
  209. end;
  210. { netware requires all allocated semaphores }
  211. { to be closed before terminating the nlm, otherwise }
  212. { the server will abend (except for netware 6 i think) }
  213. TYPE TSemaList = ARRAY [1..1000] OF LONGINT;
  214. PSemaList = ^TSemaList;
  215. CONST NumSemaOpen : LONGINT = 0;
  216. NumEntriesMax : LONGINT = 0;
  217. SemaList : PSemaList = NIL;
  218. PROCEDURE SaveSema (Handle : LONGINT);
  219. BEGIN
  220. {$ifdef DEBUG_MT}
  221. ConsolePrintf(#13'new Semaphore allocated (%x)'#13#10,Handle);
  222. {$endif DEBUG_MT}
  223. _EnterCritSec;
  224. IF NumSemaOpen = NumEntriesMax THEN
  225. BEGIN
  226. IF SemaList = NIL THEN
  227. BEGIN
  228. SemaList := _malloc (32 * SIZEOF (TSemaList[0]));
  229. NumEntriesMax := 32;
  230. END ELSE
  231. BEGIN
  232. INC (NumEntriesMax, 16);
  233. SemaList := _realloc (SemaList, NumEntriesMax * SIZEOF (TSemaList[0]));
  234. END;
  235. END;
  236. INC (NumSemaOpen);
  237. SemaList^[NumSemaOpen] := Handle;
  238. _ExitCritSec;
  239. END;
  240. PROCEDURE ReleaseSema (Handle : LONGINT);
  241. VAR I : LONGINT;
  242. BEGIN
  243. {$ifdef DEBUG_MT}
  244. ConsolePrintf(#13'Semaphore released (%x)'#13#10,Handle);
  245. {$endif DEBUG_MT}
  246. _EnterCritSec;
  247. IF SemaList <> NIL then
  248. if NumSemaOpen > 0 then
  249. begin
  250. for i := 1 to NumSemaOpen do
  251. if SemaList^[i] = Handle then
  252. begin
  253. if i < NumSemaOpen then
  254. SemaList^[i] := SemaList^[NumSemaOpen];
  255. dec (NumSemaOpen);
  256. _ExitCritSec;
  257. exit;
  258. end;
  259. end;
  260. _ExitCritSec;
  261. ConsolePrintf (#13'fpc-rtl: ReleaseSema, Handle not found'#13#10,0);
  262. END;
  263. PROCEDURE CloseAllRemainingSemaphores;
  264. var i : LONGINT;
  265. begin
  266. IF SemaList <> NIL then
  267. begin
  268. if NumSemaOpen > 0 then
  269. for i := 1 to NumSemaOpen do
  270. _CloseLocalSemaphore (SemaList^[i]);
  271. _free (SemaList);
  272. SemaList := NIL;
  273. NumSemaOpen := 0;
  274. NumEntriesMax := 0;
  275. end;
  276. end;
  277. { this allows to do a lot of things in MT safe way }
  278. { it is also used to make the heap management }
  279. { thread safe }
  280. procedure InitCriticalSection(var cs : TRTLCriticalSection);
  281. begin
  282. cs.SemaHandle := _OpenLocalSemaphore (1);
  283. if cs.SemaHandle <> 0 then
  284. begin
  285. cs.SemaIsOpen := true;
  286. SaveSema (cs.SemaHandle);
  287. end else
  288. begin
  289. cs.SemaIsOpen := false;
  290. ConsolePrintf (#13'fpc-rtl: InitCriticalsection, OpenLocalSemaphore returned error'#13#10,0);
  291. end;
  292. end;
  293. procedure DoneCriticalsection(var cs : TRTLCriticalSection);
  294. begin
  295. if cs.SemaIsOpen then
  296. begin
  297. _CloseLocalSemaphore (cs.SemaHandle);
  298. ReleaseSema (cs.SemaHandle);
  299. cs.SemaIsOpen := FALSE;
  300. end;
  301. end;
  302. procedure EnterCriticalsection(var cs : TRTLCriticalSection);
  303. begin
  304. if cs.SemaIsOpen then
  305. _WaitOnLocalSemaphore (cs.SemaHandle)
  306. else
  307. ConsolePrintf (#13'fpc-rtl: EnterCriticalsection, TRTLCriticalSection not open'#13#10,0);
  308. end;
  309. procedure LeaveCriticalsection(var cs : TRTLCriticalSection);
  310. begin
  311. if cs.SemaIsOpen then
  312. _SignalLocalSemaphore (cs.SemaHandle)
  313. else
  314. ConsolePrintf (#13'fpc-rtl: LeaveCriticalsection, TRTLCriticalSection not open'#13#10,0);
  315. end;
  316. {$endif MT}
  317. {
  318. $Log$
  319. Revision 1.4 2002-04-01 15:20:08 armin
  320. + unload module no longer shows: Module did not release...
  321. + check-function will no longer be removed when smartlink is on
  322. Revision 1.3 2002/04/01 10:47:31 armin
  323. makefile.fpc for netware
  324. stderr to netware console
  325. free all memory (threadvars and heap) to avoid error message while unloading nlm
  326. Revision 1.2 2002/03/28 16:11:17 armin
  327. + initialize threadvars defined local in units
  328. Revision 1.1 2002/03/17 17:57:33 armin
  329. + threads and winsock2 implemented
  330. }