thread.inc 9.8 KB

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