thread.inc 9.5 KB

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