thread.inc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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. ThreadID: longint;
  143. begin
  144. ReleaseThreadVars;
  145. DosGetInfoBlocks (@PTIB, nil);
  146. ThreadID := PTIB^.TIB2^.TID;
  147. {$IFDEF EMX}
  148. {$ASMMODE INTEL}
  149. if os_mode = osOS2 then
  150. asm
  151. mov eax, 7F2Dh
  152. mov edx, ThreadID
  153. call syscall
  154. end;
  155. {$ASMMODE DEFAULT}
  156. {$ENDIF EMX}
  157. end;
  158. function ThreadMain (Param: pointer): dword; cdecl;
  159. var
  160. TI: TThreadInfo;
  161. begin
  162. {$ifdef DEBUG_MT}
  163. WriteLn ('New thread started, initialising ...');
  164. {$endif DEBUG_MT}
  165. AllocateThreadVars;
  166. InitThread;
  167. TI := PThreadInfo (Param)^;
  168. Dispose (PThreadInfo (Param));
  169. {$ifdef DEBUG_MT}
  170. WriteLn ('Jumping to thread function');
  171. {$endif DEBUG_MT}
  172. ThreadMain := TI.F (TI.P);
  173. end;
  174. function BeginThread (SA: pointer; StackSize: dword;
  175. ThreadFunction: TThreadFunc; P: pointer; CreationFlags: dword;
  176. var ThreadID: dword): dword;
  177. var
  178. TI: PThreadInfo;
  179. begin
  180. {$ifdef DEBUG_MT}
  181. WriteLn ('Creating new thread');
  182. {$endif DEBUG_MT}
  183. IsMultiThread := true;
  184. { the only way to pass data to the newly created thread }
  185. { in a MT safe way, is to use the heap }
  186. New (TI);
  187. TI^.F := ThreadFunction;
  188. TI^.P := P;
  189. {$ifdef DEBUG_MT}
  190. WriteLn ('Starting new thread');
  191. {$endif DEBUG_MT}
  192. BeginThread := DosCreateThread (ThreadID, @ThreadMain, TI, CreationFlags,
  193. StackSize);
  194. {$IFDEF EMX}
  195. {$ASMMODE INTEL}
  196. asm
  197. mov eax, 7F2Ch
  198. mov edx, ThreadID
  199. call syscall
  200. end;
  201. {$ASMMODE DEFAULT}
  202. {$ENDIF EMX}
  203. end;
  204. function BeginThread (ThreadFunction: TThreadFunc): dword;
  205. var
  206. Dummy: dword;
  207. begin
  208. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  209. BeginThread := BeginThread (nil, 0, ThreadFunction, nil, dtStack_Commited,
  210. Dummy);
  211. end;
  212. function BeginThread (ThreadFunction: TThreadFunc; P: pointer): dword;
  213. var
  214. Dummy: dword;
  215. begin
  216. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  217. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, Dummy);
  218. end;
  219. function BeginThread (ThreadFunction: TThreadFunc; P: pointer;
  220. var ThreadID: dword): dword;
  221. begin
  222. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  223. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, ThreadID);
  224. end;
  225. procedure EndThread (ExitCode: dword);
  226. begin
  227. DoneThread;
  228. DosExit (0, ExitCode);
  229. end;
  230. procedure EndThread;
  231. begin
  232. EndThread (0);
  233. end;
  234. procedure InitCriticalSection (var CS: TRTLCriticalSection);
  235. begin
  236. if os_mode = osOS2 then
  237. begin
  238. if DosCreateMutExSem (nil, CS.LockSemaphore2, 0, true) <> 0 then
  239. HandleError (8);
  240. DosEnterCritSec;
  241. CS.LockCount := 0;
  242. CS.OwningThread := $FFFF;
  243. DosExitCritSec;
  244. DosReleaseMutexSem (CS.LockSemaphore2);
  245. end;
  246. end;
  247. procedure DoneCriticalSection (var CS: TRTLCriticalSection);
  248. begin
  249. if os_mode = osOS2 then DosCloseMutExSem (CS.LockSemaphore2);
  250. end;
  251. procedure EnterCriticalSection (var CS: TRTLCriticalSection);
  252. var
  253. P, T, Cnt: longint;
  254. PTIB: PThreadInfoBlock;
  255. begin
  256. if os_mode = osOS2 then
  257. begin
  258. DosGetInfoBlocks (@PTIB, nil);
  259. DosEnterCritSec;
  260. with CS do if (LockCount = 0) and
  261. (DosQueryMutExSem (LockSemaphore2, P, T, Cnt) = 0) and (Cnt = 0) and
  262. (T = PTIB^.TIB2^.TID) then
  263. begin
  264. LockCount := 1;
  265. OwningThread2 := PTIB^.TIB2^.TID;
  266. DosExitCritSec;
  267. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  268. end else if PTIB^.TIB2^.TID = OwningThread2 then
  269. begin
  270. Inc (LockCount);
  271. if LockCount = 0 then Dec (LockCount);
  272. DosExitCritSec;
  273. end else
  274. begin
  275. DosExitCritSec;
  276. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  277. DosEnterCritSec;
  278. LockCount := 1;
  279. OwningThread2 := PTIB^.TIB2^.TID;
  280. DosExitCritSec;
  281. end;
  282. end;
  283. end;
  284. procedure LeaveCriticalSection (var CS: TRTLCriticalSection);
  285. var
  286. PTIB: PThreadInfoBlock;
  287. Err: boolean;
  288. begin
  289. if os_mode = osOS2 then
  290. begin
  291. Err := false;
  292. DosGetInfoBlocks (@PTIB, nil);
  293. DosEnterCritSec;
  294. with CS do if OwningThread2 <> PTIB^.TIB2^.TID then
  295. begin
  296. DosExitCritSec;
  297. Err := true;
  298. end else if LockCount = 1 then
  299. begin
  300. if DosReleaseMutExSem (LockSemaphore2) <> 0 then Err := true;
  301. Dec (LockCount);
  302. DosExitCritSec;
  303. end else
  304. begin
  305. Dec (LockCount);
  306. DosExitCritSec;
  307. end;
  308. if Err then HandleError (5);
  309. end;
  310. end;
  311. {$ENDIF MT}
  312. {
  313. $Log$
  314. Revision 1.9 2002-09-07 16:01:25 peter
  315. * old logs removed and tabs fixed
  316. Revision 1.8 2002/07/07 18:04:39 hajny
  317. * correction by Yuri Prokushev
  318. Revision 1.7 2002/03/28 16:34:29 armin
  319. + initialize threadvars defined local in units
  320. }