tthread.inc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {****************************************************************************}
  11. {* TThread *}
  12. {****************************************************************************}
  13. {$ifdef FPC_WASM_THREADS}
  14. procedure TThread.CallOnTerminate;
  15. begin
  16. FOnTerminate(self);
  17. end;
  18. function TThread.GetPriority: TThreadPriority;
  19. begin
  20. GetPriority:=tpNormal;
  21. end;
  22. procedure TThread.SetPriority(Value: TThreadPriority);
  23. begin
  24. // Not supported
  25. end;
  26. procedure TThread.SetSuspended(Value: Boolean);
  27. begin
  28. if Value <> FSuspended then
  29. if Value then
  30. Suspend
  31. else
  32. Resume;
  33. end;
  34. procedure TThread.DoTerminate;
  35. begin
  36. if Assigned(FOnTerminate) then
  37. Synchronize(@CallOnTerminate);
  38. end;
  39. function ThreadFunc(parameter: Pointer): ptrint;
  40. Var
  41. LThread : TThread Absolute parameter;
  42. LFreeOnTerminate : Boolean;
  43. begin
  44. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('In threadfunc. Thread object: '+IntToStr(PTrUint(LThread))+' thread id :'+IntToStr(ptrint(Lthread.FThreadID)));{$ENDIF}
  45. try
  46. if LThread.FInitialSuspended then
  47. begin
  48. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('thread '+IntToStr(ptruint(LThread))+' waiting for RTLEvent '+IntToStr(ptruint(LThread.FSuspendEvent)));{$ENDIF}
  49. RtlEventWaitFor(LThread.FSuspendEvent);
  50. if (LThread.FTerminated) then
  51. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('initially created suspended, but already terminated'){$ENDIF}
  52. else if LThread.FSuspended then
  53. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('thread '+IntToStr(PtrUint(LThread))+' initially created suspended, resumed, but still suspended?!'){$ENDIF}
  54. else
  55. begin
  56. LThread.FInitialSuspended := false;
  57. CurrentThreadVar := LThread;
  58. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('going into LThread.Execute (1)');{$ENDIF}
  59. LThread.Execute;
  60. end
  61. end
  62. else
  63. begin
  64. // The suspend internal is needed due to bug 16884
  65. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Suspending internally');{$ENDIF}
  66. LThread.FSuspendedInternal:=True;
  67. RtlEventWaitFor(LThread.FSuspendEvent);
  68. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Internal suspend done.');{$ENDIF}
  69. CurrentThreadVar := LThread;
  70. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('going into LThread.Execute (2)');{$ENDIF}
  71. LThread.Execute;
  72. end;
  73. except
  74. on e: exception do
  75. begin
  76. LThread.FFatalException := TObject(AcquireExceptionObject);
  77. if e is EThreadDestroyCalled then
  78. LThread.FFreeOnTerminate := true;
  79. end;
  80. end;
  81. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('thread done running');{$ENDIF}
  82. Result := LThread.FReturnValue;
  83. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Result is '+IntToStr(Result));{$ENDIF}
  84. LFreeOnTerminate := LThread.FreeOnTerminate;
  85. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Calling doterminate');{$ENDIF}
  86. LThread.DoTerminate;
  87. LThread.FFinished := True;
  88. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Thread Set to finished');{$ENDIF}
  89. if LFreeOnTerminate then
  90. begin
  91. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Thread '+IntToStr(ptruint(lthread))+' should be freed');{$ENDIF}
  92. LThread.Free;
  93. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Thread freed');{$ENDIF}
  94. end;
  95. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Thread func calling EndThread');{$ENDIF}
  96. EndThread(Result);
  97. end;
  98. procedure TThread.SysCreate(CreateSuspended: Boolean; const StackSize: SizeUInt);
  99. begin
  100. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('In TThread.SysCreate');{$ENDIF}
  101. FSuspendEvent := RtlEventCreate;
  102. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysCreate: Created suspend event');{$ENDIF}
  103. FSuspended := CreateSuspended;
  104. FThreadReaped := false;
  105. FInitialSuspended := CreateSuspended;
  106. FSuspendedInternal := not CreateSuspended;
  107. FFatalException := nil;
  108. FHandle:=BeginThread(@ThreadFunc, Pointer(Self), FThreadID, StackSize);
  109. if FHandle = TThreadID(0) then
  110. begin
  111. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysCreate: Failed to create thread');{$ENDIF}
  112. raise EThread.create('Failed to create new thread');
  113. end;
  114. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysCreate: thread created');{$ENDIF}
  115. end;
  116. procedure TThread.SysDestroy;
  117. begin
  118. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: enter');{$ENDIF}
  119. { exception in constructor }
  120. if not assigned(FSuspendEvent) then
  121. exit;
  122. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: have suspendevent');{$ENDIF}
  123. { exception in constructor }
  124. if (FHandle = TThreadID(0)) then
  125. begin
  126. RtlEventDestroy(FSuspendEvent);
  127. exit;
  128. end;
  129. { Thread itself called destroy ? }
  130. if (FThreadID = GetCurrentThreadID) then
  131. begin
  132. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: '+IntToStr(PtrInt(FThreadID))+' = '+IntToStr(PtrInt(GetCurrentThreadID)));{$ENDIF}
  133. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: thread itself is freeing');{$ENDIF}
  134. if not(FFreeOnTerminate) and not FFinished then
  135. begin
  136. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: error condition');{$ENDIF}
  137. raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
  138. end;
  139. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: clearing FreeOnTerminate');{$ENDIF}
  140. FFreeOnTerminate := false;
  141. end
  142. else
  143. begin
  144. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: other thread is freeing');{$ENDIF}
  145. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: '+IntToStr(PtrInt(FThreadID))+' = '+IntToStr(PtrInt(GetCurrentThreadID)));{$ENDIF}
  146. { avoid recursion}
  147. FFreeOnTerminate := false;
  148. { you can't join yourself, so only for FThreadID<>GetCurrentThreadID }
  149. { and you can't join twice -> make sure we didn't join already }
  150. if not FThreadReaped then
  151. begin
  152. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: reaping thread');{$ENDIF}
  153. Terminate;
  154. if (FSuspendedInternal or FInitialSuspended) then
  155. begin
  156. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: resuming thread in order to reap');{$ENDIF}
  157. Resume;
  158. end;
  159. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: waiting on thread');{$ENDIF}
  160. // Before calling WaitFor, signal main thread with WakeMainThread, so pending checksynchronize calls are handled.
  161. if assigned(WakeMainThread) then
  162. WakeMainThread(Self);
  163. WaitFor;
  164. end;
  165. end;
  166. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: destroying RTL suspend event');{$ENDIF}
  167. RtlEventDestroy(FSuspendEvent);
  168. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('TThread.SysDestroy: freeing fatal exception if it exists');{$ENDIF}
  169. FFatalException.Free;
  170. FFatalException := nil;
  171. // Free resources associated with thread.
  172. // This must be done after EndThread is called, but that is called in ThreadFunc
  173. CloseThread(FHandle);
  174. end;
  175. procedure TThread.Resume;
  176. begin
  177. if FSuspendedInternal and (InterLockedExchange(longint(FSuspendedInternal),ord(false)) = longint(longbool(true))) then
  178. begin
  179. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('resuming thread after TThread construction '+IntToStr(ptruint(self)));{$ENDIF}
  180. RtlEventSetEvent(FSuspendEvent);
  181. end
  182. else
  183. begin
  184. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('resuming thread '+IntToStr(ptruint(self)));{$ENDIF}
  185. { don't compare with ord(true) or ord(longbool(true)), }
  186. { becaue a longbool's "true" value is anyting <> false }
  187. if FSuspended and
  188. (InterLockedExchange(longint(FSuspended),longint(false)) <> longint(longbool(false))) then
  189. begin
  190. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('resuming '+IntToStr(ptruint(self)));{$ENDIF}
  191. RtlEventSetEvent(FSuspendEvent);
  192. end;
  193. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('resumed thread '+IntToStr(ptruint(self)));{$ENDIF}
  194. end
  195. end;
  196. procedure TThread.Suspend;
  197. begin
  198. if FThreadID<>GetCurrentThreadID then
  199. Raise EThread.create('Suspending one thread from inside another one is unsupported (because it is unsafe and deadlock prone) by *nix and posix operating systems');
  200. { don't compare with ord(true) or ord(longbool(true)), }
  201. { becaue a longbool's "true" value is anyting <> false }
  202. if not FSuspended and
  203. (InterLockedExchange(longint(FSuspended),longint(longbool(true))) = longint(longbool(false))) then
  204. RtlEventWaitFor(FSuspendEvent)
  205. end;
  206. function TThread.WaitFor: Integer;
  207. begin
  208. If (MainThreadID=GetCurrentThreadID) then
  209. {
  210. FFinished is set after DoTerminate, which does a synchronize of OnTerminate,
  211. so make sure synchronize works (or indeed any other synchronize that may be
  212. in progress)
  213. }
  214. While not FFinished do
  215. CheckSynchronize(100);
  216. WaitFor:=WaitForThreadTerminate(FThreadID,-1);
  217. { should actually check for errors in WaitForThreadTerminate, but no }
  218. { error api is defined for that function }
  219. FThreadReaped:=true;
  220. end;
  221. {$else FPC_WASM_THREADS}
  222. procedure TThread.CallOnTerminate;
  223. begin
  224. end;
  225. function TThread.GetPriority: TThreadPriority;
  226. begin
  227. GetPriority:=tpNormal;
  228. end;
  229. procedure TThread.SetPriority(Value: TThreadPriority);
  230. begin
  231. end;
  232. procedure TThread.SetSuspended(Value: Boolean);
  233. begin
  234. end;
  235. procedure TThread.DoTerminate;
  236. begin
  237. end;
  238. procedure TThread.SysCreate(CreateSuspended: Boolean; const StackSize: SizeUInt);
  239. begin
  240. {IsMultiThread := TRUE; }
  241. end;
  242. procedure TThread.SysDestroy;
  243. begin
  244. end;
  245. procedure TThread.Resume;
  246. begin
  247. end;
  248. procedure TThread.Suspend;
  249. begin
  250. end;
  251. function TThread.WaitFor: Integer;
  252. begin
  253. WaitFor:=0;
  254. end;
  255. {$endif FPC_WASM_THREADS}