thread.inc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by the Free Pascal development team.
  5. Multithreading implementation for OS/2
  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. const
  14. ThreadVarBlockSize: dword = 0;
  15. pag_Read = 1;
  16. pag_Write = 2;
  17. pag_Execute = 4;
  18. pag_Guard = 8;
  19. pag_Commit = $10;
  20. obj_Tile = $40;
  21. sem_Indefinite_Wait = -1;
  22. dtSuspended = 1;
  23. dtStack_Commited = 2;
  24. type
  25. TThreadInfo = record
  26. F: TThreadFunc;
  27. P: pointer;
  28. end;
  29. PThreadInfo = ^TThreadInfo;
  30. var
  31. (* Pointer to an allocated dword space within the local thread *)
  32. (* memory area. Pointer to the real memory block allocated for *)
  33. (* thread vars in this block is then stored in this dword. *)
  34. DataIndex: PPointer;
  35. { import the necessary stuff from the OS }
  36. function DosAllocThreadLocalMemory (Count: cardinal; var P: pointer): cardinal;
  37. cdecl; external 'DOSCALLS' index 454;
  38. function DosFreeThreadLocalMemory (P: pointer): cardinal; cdecl;
  39. external 'DOSCALLS' index 455;
  40. function DosCreateThread (var TID: cardinal; Address: pointer;
  41. (* TThreadFunc *)
  42. aParam: pointer; Flags: cardinal; StackSize: cardinal): cardinal; cdecl;
  43. external 'DOSCALLS' index 311;
  44. procedure DosExit (Action, Result: cardinal); cdecl;
  45. external 'DOSCALLS' index 234;
  46. function DosCreateMutExSem (Name: PChar; var Handle: longint; Attr: cardinal;
  47. State: boolean): cardinal; cdecl; external 'DOSCALLS' index 331;
  48. function DosCloseMutExSem (Handle: longint): cardinal; cdecl;
  49. external 'DOSCALLS' index 333;
  50. function DosQueryMutExSem (Handle: longint; var PID, TID, Count: cardinal):
  51. cardinal; cdecl; external 'DOSCALLS' index 336;
  52. function DosRequestMutExSem (Handle: longint; Timeout: cardinal): cardinal;
  53. cdecl; external 'DOSCALLS' index 334;
  54. function DosReleaseMutExSem (Handle: longint): cardinal; cdecl;
  55. external 'DOSCALLS' index 335;
  56. function DosAllocMem (var P: pointer; Size, Flag: cardinal): cardinal; cdecl;
  57. external 'DOSCALLS' index 299;
  58. function DosFreeMem (P: pointer): cardinal; cdecl;
  59. external 'DOSCALLS' index 304;
  60. function DosEnterCritSec:cardinal; cdecl; external 'DOSCALLS' index 232;
  61. function DosExitCritSec:cardinal; cdecl; external 'DOSCALLS' index 233;
  62. procedure Init_ThreadVar (var TVOffset: dword; Size: dword);
  63. [public, alias: 'FPC_INIT_THREADVAR'];
  64. begin
  65. TVOffset := ThreadVarBlockSize;
  66. Inc (ThreadVarBlockSize, Size);
  67. end;
  68. type ltvInitEntry =
  69. record
  70. varaddr : pdword;
  71. size : longint;
  72. end;
  73. pltvInitEntry = ^ltvInitEntry;
  74. procedure init_unit_threadvars (tableEntry : pltvInitEntry);
  75. begin
  76. while tableEntry^.varaddr <> nil do
  77. begin
  78. init_threadvar (tableEntry^.varaddr^, tableEntry^.size);
  79. inc (pchar (tableEntry), sizeof (tableEntry^));
  80. end;
  81. end;
  82. type TltvInitTablesTable =
  83. record
  84. count : dword;
  85. tables: array [1..32767] of pltvInitEntry;
  86. end;
  87. var
  88. ThreadvarTablesTable : TltvInitTablesTable; external name 'FPC_LOCALTHREADVARTABLES';
  89. procedure init_all_unit_threadvars; [public,alias: 'FPC_INITIALIZELOCALTHREADVARS'];
  90. var i : integer;
  91. begin
  92. {$ifdef DEBUG_MT}
  93. WriteLn ('init_all_unit_threadvars (%d) units',ThreadvarTablesTable.count);
  94. {$endif}
  95. for i := 1 to ThreadvarTablesTable.count do
  96. init_unit_threadvars (ThreadvarTablesTable.tables[i]);
  97. end;
  98. function Relocate_ThreadVar (TVOffset: dword): pointer;
  99. [public,alias: 'FPC_RELOCATE_THREADVAR'];
  100. begin
  101. Relocate_ThreadVar := DataIndex^ + TVOffset;
  102. end;
  103. procedure AllocateThreadVars;
  104. begin
  105. { we've to allocate the memory from the OS }
  106. { because the FPC heap management uses }
  107. { exceptions which use threadvars but }
  108. { these aren't allocated yet ... }
  109. { allocate room on the heap for the thread vars }
  110. if DosAllocMem (DataIndex^, ThreadVarBlockSize, pag_Read or pag_Write
  111. or pag_Commit) <> 0 then HandleError (8);
  112. end;
  113. procedure ReleaseThreadVars;
  114. begin
  115. { release thread vars }
  116. DosFreeMem (DataIndex^)
  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. var
  131. PTIB: PThreadInfoBlock;
  132. PPIB: PProcessInfoBlock;
  133. ThreadID: cardinal;
  134. begin
  135. ReleaseThreadVars;
  136. DosGetInfoBlocks (@PTIB, @PPIB);
  137. ThreadID := PTIB^.TIB2^.TID;
  138. end;
  139. function ThreadMain (Param: pointer): dword; cdecl;
  140. var
  141. TI: TThreadInfo;
  142. begin
  143. {$ifdef DEBUG_MT}
  144. WriteLn ('New thread started, initialising ...');
  145. {$endif DEBUG_MT}
  146. AllocateThreadVars;
  147. InitThread;
  148. TI := PThreadInfo (Param)^;
  149. Dispose (PThreadInfo (Param));
  150. {$ifdef DEBUG_MT}
  151. WriteLn ('Jumping to thread function');
  152. {$endif DEBUG_MT}
  153. ThreadMain := TI.F (TI.P);
  154. end;
  155. function BeginThread (SA: pointer; StackSize: dword;
  156. ThreadFunction: TThreadFunc; P: pointer; CreationFlags: dword;
  157. var ThreadID: dword): dword;
  158. var
  159. TI: PThreadInfo;
  160. begin
  161. {$ifdef DEBUG_MT}
  162. WriteLn ('Creating new thread');
  163. {$endif DEBUG_MT}
  164. IsMultiThread := true;
  165. { the only way to pass data to the newly created thread }
  166. { in a MT safe way, is to use the heap }
  167. New (TI);
  168. TI^.F := ThreadFunction;
  169. TI^.P := P;
  170. {$ifdef DEBUG_MT}
  171. WriteLn ('Starting new thread');
  172. {$endif DEBUG_MT}
  173. BeginThread := DosCreateThread (ThreadID, @ThreadMain, TI, CreationFlags,
  174. StackSize);
  175. end;
  176. function BeginThread (ThreadFunction: TThreadFunc): dword;
  177. var
  178. Dummy: dword;
  179. begin
  180. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  181. BeginThread := BeginThread (nil, 0, ThreadFunction, nil, dtStack_Commited,
  182. Dummy);
  183. end;
  184. function BeginThread (ThreadFunction: TThreadFunc; P: pointer): dword;
  185. var
  186. Dummy: dword;
  187. begin
  188. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  189. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, Dummy);
  190. end;
  191. function BeginThread (ThreadFunction: TThreadFunc; P: pointer;
  192. var ThreadID: dword): dword;
  193. begin
  194. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  195. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, ThreadID);
  196. end;
  197. procedure EndThread (ExitCode: dword);
  198. begin
  199. DoneThread;
  200. DosExit (0, ExitCode);
  201. end;
  202. procedure EndThread;
  203. begin
  204. EndThread (0);
  205. end;
  206. procedure InitCriticalSection (var CS: TRTLCriticalSection);
  207. begin
  208. if DosCreateMutExSem (nil, CS.LockSemaphore2, 0, true) <> 0 then
  209. HandleError (8);
  210. DosEnterCritSec;
  211. CS.LockCount := 0;
  212. CS.OwningThread := $FFFF;
  213. DosExitCritSec;
  214. DosReleaseMutexSem (CS.LockSemaphore2);
  215. end;
  216. procedure DoneCriticalSection (var CS: TRTLCriticalSection);
  217. begin
  218. DosCloseMutExSem (CS.LockSemaphore2);
  219. end;
  220. procedure EnterCriticalSection (var CS: TRTLCriticalSection);
  221. var
  222. P, T, Cnt: cardinal;
  223. PTIB: PThreadInfoBlock;
  224. PPIB: PProcessInfoBlock;
  225. begin
  226. DosGetInfoBlocks (@PTIB, @PPIB);
  227. DosEnterCritSec;
  228. with CS do if (LockCount = 0) and
  229. (DosQueryMutExSem (LockSemaphore2, P, T, Cnt) = 0) and (Cnt = 0) and
  230. (T = PTIB^.TIB2^.TID) then
  231. begin
  232. LockCount := 1;
  233. OwningThread2 := PTIB^.TIB2^.TID;
  234. DosExitCritSec;
  235. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  236. end else if PTIB^.TIB2^.TID = OwningThread2 then
  237. begin
  238. Inc (LockCount);
  239. if LockCount = 0 then Dec (LockCount);
  240. DosExitCritSec;
  241. end else
  242. begin
  243. DosExitCritSec;
  244. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  245. DosEnterCritSec;
  246. LockCount := 1;
  247. OwningThread2 := PTIB^.TIB2^.TID;
  248. DosExitCritSec;
  249. end;
  250. end;
  251. procedure LeaveCriticalSection (var CS: TRTLCriticalSection);
  252. var
  253. PTIB: PThreadInfoBlock;
  254. PPIB: PProcessInfoBlock;
  255. Err: boolean;
  256. begin
  257. Err := false;
  258. DosGetInfoBlocks (@PTIB, @PPIB);
  259. DosEnterCritSec;
  260. with CS do if OwningThread2 <> PTIB^.TIB2^.TID then
  261. begin
  262. DosExitCritSec;
  263. Err := true;
  264. end else if LockCount = 1 then
  265. begin
  266. if DosReleaseMutExSem (LockSemaphore2) <> 0 then Err := true;
  267. Dec (LockCount);
  268. DosExitCritSec;
  269. end else
  270. begin
  271. Dec (LockCount);
  272. DosExitCritSec;
  273. end;
  274. if Err then HandleError (5);
  275. end;
  276. {$ENDIF MT}
  277. {
  278. $Log$
  279. Revision 1.14 2003-10-13 21:17:31 hajny
  280. * longint to cardinal corrections
  281. Revision 1.13 2003/10/08 09:21:33 yuri
  282. * EMX code removed. Most probably, MT broken. (EMX notification removed)
  283. Revision 1.12 2003/10/08 05:22:47 yuri
  284. * Some emx code removed
  285. Revision 1.11 2003/10/07 21:26:35 hajny
  286. * stdcall fixes and asm routines cleanup
  287. Revision 1.10 2003/02/20 17:09:49 hajny
  288. * fixes for OS/2 v2.1 incompatibility
  289. Revision 1.9 2002/09/07 16:01:25 peter
  290. * old logs removed and tabs fixed
  291. Revision 1.8 2002/07/07 18:04:39 hajny
  292. * correction by Yuri Prokushev
  293. Revision 1.7 2002/03/28 16:34:29 armin
  294. + initialize threadvars defined local in units
  295. }