thread.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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 os_mode = osOS2 then
  112. begin
  113. if DosAllocMem (DataIndex^, ThreadVarBlockSize, pag_Read or pag_Write
  114. or pag_Commit) <> 0 then HandleError (8);
  115. end else
  116. begin
  117. (* Allocate the DOS memory here. *)
  118. end;
  119. end;
  120. procedure ReleaseThreadVars;
  121. begin
  122. { release thread vars }
  123. if os_mode = osOS2 then DosFreeMem (DataIndex^) else
  124. begin
  125. (* Deallocate the DOS memory here. *)
  126. end;
  127. end;
  128. procedure InitThread;
  129. begin
  130. InitFPU;
  131. { we don't need to set the data to 0 because we did this with }
  132. { the fillchar above, but it looks nicer }
  133. { ExceptAddrStack and ExceptObjectStack are threadvars }
  134. { so every thread has its on exception handling capabilities }
  135. InitExceptions;
  136. InOutRes := 0;
  137. { ErrNo := 0;}
  138. end;
  139. procedure DoneThread;
  140. var
  141. PTIB: PThreadInfoBlock;
  142. PPIB: PProcessInfoBlock;
  143. ThreadID: longint;
  144. begin
  145. ReleaseThreadVars;
  146. DosGetInfoBlocks (@PTIB, @PPIB);
  147. ThreadID := PTIB^.TIB2^.TID;
  148. {$IFDEF EMX}
  149. {$ASMMODE INTEL}
  150. if os_mode = osOS2 then
  151. asm
  152. mov eax, 7F2Dh
  153. mov edx, ThreadID
  154. call syscall
  155. end;
  156. {$ASMMODE DEFAULT}
  157. {$ENDIF EMX}
  158. end;
  159. function ThreadMain (Param: pointer): dword; cdecl;
  160. var
  161. TI: TThreadInfo;
  162. begin
  163. {$ifdef DEBUG_MT}
  164. WriteLn ('New thread started, initialising ...');
  165. {$endif DEBUG_MT}
  166. AllocateThreadVars;
  167. InitThread;
  168. TI := PThreadInfo (Param)^;
  169. Dispose (PThreadInfo (Param));
  170. {$ifdef DEBUG_MT}
  171. WriteLn ('Jumping to thread function');
  172. {$endif DEBUG_MT}
  173. ThreadMain := TI.F (TI.P);
  174. end;
  175. function BeginThread (SA: pointer; StackSize: dword;
  176. ThreadFunction: TThreadFunc; P: pointer; CreationFlags: dword;
  177. var ThreadID: dword): dword;
  178. var
  179. TI: PThreadInfo;
  180. begin
  181. {$ifdef DEBUG_MT}
  182. WriteLn ('Creating new thread');
  183. {$endif DEBUG_MT}
  184. IsMultiThread := true;
  185. { the only way to pass data to the newly created thread }
  186. { in a MT safe way, is to use the heap }
  187. New (TI);
  188. TI^.F := ThreadFunction;
  189. TI^.P := P;
  190. {$ifdef DEBUG_MT}
  191. WriteLn ('Starting new thread');
  192. {$endif DEBUG_MT}
  193. BeginThread := DosCreateThread (ThreadID, @ThreadMain, TI, CreationFlags,
  194. StackSize);
  195. {$IFDEF EMX}
  196. {$ASMMODE INTEL}
  197. asm
  198. mov eax, 7F2Ch
  199. mov edx, ThreadID
  200. call syscall
  201. end;
  202. {$ASMMODE DEFAULT}
  203. {$ENDIF EMX}
  204. end;
  205. function BeginThread (ThreadFunction: TThreadFunc): dword;
  206. var
  207. Dummy: dword;
  208. begin
  209. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  210. BeginThread := BeginThread (nil, 0, ThreadFunction, nil, dtStack_Commited,
  211. Dummy);
  212. end;
  213. function BeginThread (ThreadFunction: TThreadFunc; P: pointer): dword;
  214. var
  215. Dummy: dword;
  216. begin
  217. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  218. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, Dummy);
  219. end;
  220. function BeginThread (ThreadFunction: TThreadFunc; P: pointer;
  221. var ThreadID: dword): dword;
  222. begin
  223. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  224. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, ThreadID);
  225. end;
  226. procedure EndThread (ExitCode: dword);
  227. begin
  228. DoneThread;
  229. DosExit (0, ExitCode);
  230. end;
  231. procedure EndThread;
  232. begin
  233. EndThread (0);
  234. end;
  235. procedure InitCriticalSection (var CS: TRTLCriticalSection);
  236. begin
  237. if os_mode = osOS2 then
  238. begin
  239. if DosCreateMutExSem (nil, CS.LockSemaphore2, 0, true) <> 0 then
  240. HandleError (8);
  241. DosEnterCritSec;
  242. CS.LockCount := 0;
  243. CS.OwningThread := $FFFF;
  244. DosExitCritSec;
  245. DosReleaseMutexSem (CS.LockSemaphore2);
  246. end;
  247. end;
  248. procedure DoneCriticalSection (var CS: TRTLCriticalSection);
  249. begin
  250. if os_mode = osOS2 then DosCloseMutExSem (CS.LockSemaphore2);
  251. end;
  252. procedure EnterCriticalSection (var CS: TRTLCriticalSection);
  253. var
  254. P, T, Cnt: longint;
  255. PTIB: PThreadInfoBlock;
  256. PPIB: PProcessInfoBlock;
  257. begin
  258. if os_mode = osOS2 then
  259. begin
  260. DosGetInfoBlocks (@PTIB, @PPIB);
  261. DosEnterCritSec;
  262. with CS do if (LockCount = 0) and
  263. (DosQueryMutExSem (LockSemaphore2, P, T, Cnt) = 0) and (Cnt = 0) and
  264. (T = PTIB^.TIB2^.TID) then
  265. begin
  266. LockCount := 1;
  267. OwningThread2 := PTIB^.TIB2^.TID;
  268. DosExitCritSec;
  269. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  270. end else if PTIB^.TIB2^.TID = OwningThread2 then
  271. begin
  272. Inc (LockCount);
  273. if LockCount = 0 then Dec (LockCount);
  274. DosExitCritSec;
  275. end else
  276. begin
  277. DosExitCritSec;
  278. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  279. DosEnterCritSec;
  280. LockCount := 1;
  281. OwningThread2 := PTIB^.TIB2^.TID;
  282. DosExitCritSec;
  283. end;
  284. end;
  285. end;
  286. procedure LeaveCriticalSection (var CS: TRTLCriticalSection);
  287. var
  288. PTIB: PThreadInfoBlock;
  289. PPIB: PProcessInfoBlock;
  290. Err: boolean;
  291. begin
  292. if os_mode = osOS2 then
  293. begin
  294. Err := false;
  295. DosGetInfoBlocks (@PTIB, @PPIB);
  296. DosEnterCritSec;
  297. with CS do if OwningThread2 <> PTIB^.TIB2^.TID then
  298. begin
  299. DosExitCritSec;
  300. Err := true;
  301. end else if LockCount = 1 then
  302. begin
  303. if DosReleaseMutExSem (LockSemaphore2) <> 0 then Err := true;
  304. Dec (LockCount);
  305. DosExitCritSec;
  306. end else
  307. begin
  308. Dec (LockCount);
  309. DosExitCritSec;
  310. end;
  311. if Err then HandleError (5);
  312. end;
  313. end;
  314. {$ENDIF MT}
  315. {
  316. $Log$
  317. Revision 1.10 2003-02-20 17:09:49 hajny
  318. * fixes for OS/2 v2.1 incompatibility
  319. Revision 1.9 2002/09/07 16:01:25 peter
  320. * old logs removed and tabs fixed
  321. Revision 1.8 2002/07/07 18:04:39 hajny
  322. * correction by Yuri Prokushev
  323. Revision 1.7 2002/03/28 16:34:29 armin
  324. + initialize threadvars defined local in units
  325. }