thread.inc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 233;
  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. function Relocate_ThreadVar (TVOffset: dword): pointer;
  70. [public,alias: 'FPC_RELOCATE_THREADVAR'];
  71. begin
  72. Relocate_ThreadVar := DataIndex^ + TVOffset;
  73. end;
  74. procedure AllocateThreadVars;
  75. begin
  76. { we've to allocate the memory from the OS }
  77. { because the FPC heap management uses }
  78. { exceptions which use threadvars but }
  79. { these aren't allocated yet ... }
  80. { allocate room on the heap for the thread vars }
  81. if os_mode = osOS2 then
  82. begin
  83. if DosAllocMem (DataIndex^, ThreadVarBlockSize, pag_Read or pag_Write
  84. or pag_Commit) <> 0 then HandleError (8);
  85. end else
  86. begin
  87. (* Allocate the DOS memory here. *)
  88. end;
  89. end;
  90. procedure ReleaseThreadVars;
  91. begin
  92. { release thread vars }
  93. if os_mode = osOS2 then DosFreeMem (DataIndex^) else
  94. begin
  95. (* Deallocate the DOS memory here. *)
  96. end;
  97. end;
  98. procedure InitThread;
  99. begin
  100. InitFPU;
  101. { we don't need to set the data to 0 because we did this with }
  102. { the fillchar above, but it looks nicer }
  103. { ExceptAddrStack and ExceptObjectStack are threadvars }
  104. { so every thread has its on exception handling capabilities }
  105. InitExceptions;
  106. InOutRes := 0;
  107. { ErrNo := 0;}
  108. end;
  109. procedure DoneThread;
  110. var
  111. PTIB: PThreadInfoBlock;
  112. ThreadID: longint;
  113. begin
  114. ReleaseThreadVars;
  115. DosGetInfoBlocks (@PTIB, nil);
  116. ThreadID := PTIB^.TIB2^.TID;
  117. {$IFDEF EMX}
  118. {$ASMMODE INTEL}
  119. if os_mode = osOS2 then
  120. asm
  121. mov eax, 7F2Dh
  122. mov edx, ThreadID
  123. call syscall
  124. end;
  125. {$ASMMODE DEFAULT}
  126. {$ENDIF EMX}
  127. end;
  128. function ThreadMain (Param: pointer): dword; cdecl;
  129. var
  130. TI: TThreadInfo;
  131. begin
  132. {$ifdef DEBUG_MT}
  133. WriteLn ('New thread started, initialising ...');
  134. {$endif DEBUG_MT}
  135. AllocateThreadVars;
  136. InitThread;
  137. TI := PThreadInfo (Param)^;
  138. Dispose (PThreadInfo (Param));
  139. {$ifdef DEBUG_MT}
  140. WriteLn ('Jumping to thread function');
  141. {$endif DEBUG_MT}
  142. ThreadMain := TI.F (TI.P);
  143. end;
  144. function BeginThread (SA: pointer; StackSize: dword;
  145. ThreadFunction: TThreadFunc; P: pointer; CreationFlags: dword;
  146. var ThreadID: dword): dword;
  147. var
  148. TI: PThreadInfo;
  149. begin
  150. {$ifdef DEBUG_MT}
  151. WriteLn ('Creating new thread');
  152. {$endif DEBUG_MT}
  153. IsMultiThread := true;
  154. { the only way to pass data to the newly created thread }
  155. { in a MT safe way, is to use the heap }
  156. New (TI);
  157. TI^.F := ThreadFunction;
  158. TI^.P := P;
  159. {$ifdef DEBUG_MT}
  160. WriteLn ('Starting new thread');
  161. {$endif DEBUG_MT}
  162. BeginThread := DosCreateThread (ThreadID, @ThreadMain, TI, CreationFlags,
  163. StackSize);
  164. {$IFDEF EMX}
  165. {$ASMMODE INTEL}
  166. asm
  167. mov eax, 7F2Ch
  168. mov edx, ThreadID
  169. call syscall
  170. end;
  171. {$ASMMODE DEFAULT}
  172. {$ENDIF EMX}
  173. end;
  174. function BeginThread (ThreadFunction: TThreadFunc): dword;
  175. var
  176. Dummy: dword;
  177. begin
  178. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  179. BeginThread := BeginThread (nil, 0, ThreadFunction, nil, dtStack_Commited,
  180. Dummy);
  181. end;
  182. function BeginThread (ThreadFunction: TThreadFunc; P: pointer): dword;
  183. var
  184. Dummy: dword;
  185. begin
  186. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  187. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, Dummy);
  188. end;
  189. function BeginThread (ThreadFunction: TThreadFunc; P: pointer;
  190. var ThreadID: dword): dword;
  191. begin
  192. (* The stack size of 0 causes 4 kB to be allocated for stack. *)
  193. BeginThread := BeginThread (nil, 0, ThreadFunction, P, 0, ThreadID);
  194. end;
  195. procedure EndThread (ExitCode: dword);
  196. begin
  197. DoneThread;
  198. DosExit (0, ExitCode);
  199. end;
  200. procedure EndThread;
  201. begin
  202. EndThread (0);
  203. end;
  204. procedure InitCriticalSection (var CS: TRTLCriticalSection);
  205. begin
  206. if os_mode = osOS2 then
  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. end;
  217. procedure DoneCriticalSection (var CS: TRTLCriticalSection);
  218. begin
  219. if os_mode = osOS2 then DosCloseMutExSem (CS.LockSemaphore2);
  220. end;
  221. procedure EnterCriticalSection (var CS: TRTLCriticalSection);
  222. var
  223. P, T, Cnt: longint;
  224. PTIB: PThreadInfoBlock;
  225. begin
  226. if os_mode = osOS2 then
  227. begin
  228. DosGetInfoBlocks (@PTIB, nil);
  229. DosEnterCritSec;
  230. with CS do if (LockCount = 0) and
  231. (DosQueryMutExSem (LockSemaphore2, P, T, Cnt) = 0) and (Cnt = 0) and
  232. (T = PTIB^.TIB2^.TID) then
  233. begin
  234. LockCount := 1;
  235. OwningThread2 := PTIB^.TIB2^.TID;
  236. DosExitCritSec;
  237. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  238. end else if PTIB^.TIB2^.TID = OwningThread2 then
  239. begin
  240. Inc (LockCount);
  241. if LockCount = 0 then Dec (LockCount);
  242. DosExitCritSec;
  243. end else
  244. begin
  245. DosExitCritSec;
  246. DosRequestMutExSem (LockSemaphore2, sem_Indefinite_Wait);
  247. DosEnterCritSec;
  248. LockCount := 1;
  249. OwningThread2 := PTIB^.TIB2^.TID;
  250. DosExitCritSec;
  251. end;
  252. end;
  253. end;
  254. procedure LeaveCriticalSection (var CS: TRTLCriticalSection);
  255. var
  256. PTIB: PThreadInfoBlock;
  257. Err: boolean;
  258. begin
  259. if os_mode = osOS2 then
  260. begin
  261. Err := false;
  262. DosGetInfoBlocks (@PTIB, nil);
  263. DosEnterCritSec;
  264. with CS do if OwningThread2 <> PTIB^.TIB2^.TID then
  265. begin
  266. DosExitCritSec;
  267. Err := true;
  268. end else if LockCount = 1 then
  269. begin
  270. if DosReleaseMutExSem (LockSemaphore2) <> 0 then Err := true;
  271. Dec (LockCount);
  272. DosExitCritSec;
  273. end else
  274. begin
  275. Dec (LockCount);
  276. DosExitCritSec;
  277. end;
  278. if Err then HandleError (5);
  279. end;
  280. end;
  281. {$ENDIF MT}
  282. {
  283. $Log$
  284. Revision 1.6 2001-10-23 21:51:03 peter
  285. * criticalsection renamed to rtlcriticalsection for kylix compatibility
  286. Revision 1.5 2001/10/09 02:42:05 carl
  287. * bugfix #1639 (IsMultiThread varialbe setting)
  288. Revision 1.4 2001/02/04 01:53:58 hajny
  289. * HandleError instead of RunError
  290. Revision 1.3 2001/02/01 21:30:01 hajny
  291. * MT support completion
  292. Revision 1.2 2001/01/27 18:28:52 hajny
  293. * OS/2 implementation of threads almost finished
  294. Revision 1.1 2001/01/23 20:38:59 hajny
  295. + beginning of the OS/2 version
  296. Revision 1.1 2001/01/01 19:06:36 florian
  297. + initial release
  298. }