tthread.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. {
  2. $Id$
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by Peter Vreman
  5. Darwin TThread implementation
  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 VER1_0} // leaving the old implementation in for now...
  13. type
  14. PThreadRec=^TThreadRec;
  15. TThreadRec=record
  16. thread : TThread;
  17. next : PThreadRec;
  18. end;
  19. var
  20. ThreadRoot : PThreadRec;
  21. ThreadsInited : boolean;
  22. // MainThreadID: longint;
  23. Const
  24. ThreadCount: longint = 0;
  25. function ThreadSelf:TThread;
  26. var
  27. hp : PThreadRec;
  28. sp : Pointer;
  29. begin
  30. sp:=SPtr;
  31. hp:=ThreadRoot;
  32. while assigned(hp) do
  33. begin
  34. if (sp<=hp^.Thread.FStackPointer) and
  35. (sp>(hp^.Thread.FStackPointer-hp^.Thread.FStackSize)) then
  36. begin
  37. Result:=hp^.Thread;
  38. exit;
  39. end;
  40. hp:=hp^.next;
  41. end;
  42. Result:=nil;
  43. end;
  44. //function SIGCHLDHandler(Sig: longint): longint; cdecl;//this is std linux C declaration as function
  45. procedure SIGCHLDHandler(Sig: longint); cdecl;
  46. begin
  47. fpwaitpid(-1, nil, WNOHANG);
  48. end;
  49. procedure InitThreads;
  50. var
  51. Act, OldAct: Baseunix.PSigActionRec;
  52. begin
  53. ThreadRoot:=nil;
  54. ThreadsInited:=true;
  55. // This will install SIGCHLD signal handler
  56. // signal() installs "one-shot" handler,
  57. // so it is better to install and set up handler with sigaction()
  58. GetMem(Act, SizeOf(SigActionRec));
  59. GetMem(OldAct, SizeOf(SigActionRec));
  60. Act^.sa_handler := TSigAction(@SIGCHLDHandler);
  61. Act^.sa_flags := SA_NOCLDSTOP {or SA_NOMASK or SA_RESTART};
  62. Fillchar(Act^.sa_mask,sizeof(Act^.sa_mask),0); //Do not block all signals ??. Don't need if SA_NOMASK in flags
  63. FpSigAction(SIGCHLD, Act, OldAct);
  64. FreeMem(Act, SizeOf(SigActionRec));
  65. FreeMem(OldAct, SizeOf(SigActionRec));
  66. end;
  67. procedure DoneThreads;
  68. var
  69. hp : PThreadRec;
  70. begin
  71. while assigned(ThreadRoot) do
  72. begin
  73. ThreadRoot^.Thread.Destroy;
  74. hp:=ThreadRoot;
  75. ThreadRoot:=ThreadRoot^.Next;
  76. dispose(hp);
  77. end;
  78. ThreadsInited:=false;
  79. end;
  80. procedure AddThread(t:TThread);
  81. var
  82. hp : PThreadRec;
  83. begin
  84. { Need to initialize threads ? }
  85. if not ThreadsInited then
  86. InitThreads;
  87. { Put thread in the linked list }
  88. new(hp);
  89. hp^.Thread:=t;
  90. hp^.next:=ThreadRoot;
  91. ThreadRoot:=hp;
  92. inc(ThreadCount, 1);
  93. end;
  94. procedure RemoveThread(t:TThread);
  95. var
  96. lasthp,hp : PThreadRec;
  97. begin
  98. hp:=ThreadRoot;
  99. lasthp:=nil;
  100. while assigned(hp) do
  101. begin
  102. if hp^.Thread=t then
  103. begin
  104. if assigned(lasthp) then
  105. lasthp^.next:=hp^.next
  106. else
  107. ThreadRoot:=hp^.next;
  108. dispose(hp);
  109. exit;
  110. end;
  111. lasthp:=hp;
  112. hp:=hp^.next;
  113. end;
  114. Dec(ThreadCount, 1);
  115. if ThreadCount = 0 then DoneThreads;
  116. end;
  117. { TThread }
  118. function ThreadProc(args:pointer): Integer;cdecl;
  119. var
  120. FreeThread: Boolean;
  121. Thread : TThread absolute args;
  122. begin
  123. while Thread.FHandle = 0 do fpsleep(1);
  124. if Thread.FSuspended then Thread.suspend();
  125. try
  126. Thread.Execute;
  127. except
  128. Thread.FFatalException := TObject(AcquireExceptionObject);
  129. end;
  130. FreeThread := Thread.FFreeOnTerminate;
  131. Result := Thread.FReturnValue;
  132. Thread.FFinished := True;
  133. Thread.DoTerminate;
  134. if FreeThread then
  135. Thread.Free;
  136. fpexit(Result);
  137. end;
  138. constructor TThread.Create(CreateSuspended: Boolean);
  139. var
  140. Flags: Integer;
  141. begin
  142. inherited Create;
  143. AddThread(self);
  144. FSuspended := CreateSuspended;
  145. Flags := CLONE_VM + CLONE_FS + CLONE_FILES + CLONE_SIGHAND + SIGCHLD;
  146. { Setup 16k of stack }
  147. FStackSize:=16384;
  148. Getmem(FStackPointer,FStackSize);
  149. inc(FStackPointer,FStackSize);
  150. FCallExitProcess:=false;
  151. { Clone }
  152. FHandle:= Clone(@ThreadProc,FStackPointer,Flags,self);
  153. // if FSuspended then Suspend;
  154. FThreadID := FHandle;
  155. IsMultiThread := TRUE;
  156. FFatalException := nil;
  157. end;
  158. destructor TThread.Destroy;
  159. begin
  160. if not FFinished and not Suspended then
  161. begin
  162. Terminate;
  163. WaitFor;
  164. end;
  165. if FHandle <> -1 then
  166. fpkill(FHandle, SIGKILL);
  167. dec(FStackPointer,FStackSize);
  168. Freemem(FStackPointer);
  169. FFatalException.Free;
  170. FFatalException := nil;
  171. inherited Destroy;
  172. RemoveThread(self);
  173. end;
  174. procedure TThread.CallOnTerminate;
  175. begin
  176. FOnTerminate(Self);
  177. end;
  178. procedure TThread.DoTerminate;
  179. begin
  180. if Assigned(FOnTerminate) then
  181. Synchronize(@CallOnTerminate);
  182. end;
  183. const
  184. { I Don't know idle or timecritical, value is also 20, so the largest other
  185. possibility is 19 (PFV) }
  186. Priorities: array [TThreadPriority] of Integer =
  187. (-20,-19,-10,9,10,19,20);
  188. function TThread.GetPriority: TThreadPriority;
  189. var
  190. P: Integer;
  191. I: TThreadPriority;
  192. begin
  193. P := fpGetPriority(Prio_Process,FHandle);
  194. Result := tpNormal;
  195. for I := Low(TThreadPriority) to High(TThreadPriority) do
  196. if Priorities[I] = P then
  197. Result := I;
  198. end;
  199. procedure TThread.SetPriority(Value: TThreadPriority);
  200. begin
  201. fpSetPriority(Prio_Process,FHandle,Priorities[Value]);
  202. end;
  203. procedure TThread.Synchronize(Method: TThreadMethod);
  204. begin
  205. FSynchronizeException := nil;
  206. FMethod := Method;
  207. { SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self)); }
  208. if Assigned(FSynchronizeException) then
  209. raise FSynchronizeException;
  210. end;
  211. procedure TThread.SetSuspended(Value: Boolean);
  212. begin
  213. if Value <> FSuspended then
  214. if Value then
  215. Suspend
  216. else
  217. Resume;
  218. end;
  219. procedure TThread.Suspend;
  220. begin
  221. FSuspended := true;
  222. fpKill(FHandle, SIGSTOP);
  223. end;
  224. procedure TThread.Resume;
  225. begin
  226. fpKill(FHandle, SIGCONT);
  227. FSuspended := False;
  228. end;
  229. procedure TThread.Terminate;
  230. begin
  231. FTerminated := True;
  232. end;
  233. function TThread.WaitFor: Integer;
  234. var
  235. status : longint;
  236. begin
  237. if FThreadID = MainThreadID then
  238. fpwaitpid(0,@status,0)
  239. else
  240. fpwaitpid(FHandle,@status,0);
  241. Result:=status;
  242. end;
  243. {$ELSE}
  244. {
  245. What follows, is a short description on my implementation of TThread.
  246. Most information can also be found by reading the source and accompanying
  247. comments.
  248. A thread is created using BeginThread, which in turn calls
  249. pthread_create. So the threads here are always posix threads.
  250. Posix doesn't define anything for suspending threads as this is
  251. inherintly unsafe. Just don't suspend threads at points they cannot
  252. control. Therefore, I didn't implement .Suspend() if its called from
  253. outside the threads execution flow (except on Linux _without_ NPTL).
  254. The implementation for .suspend uses a semaphore, which is initialized
  255. at thread creation. If the thread tries to suspend itself, we simply
  256. let it wait on the semaphore until it is unblocked by someone else
  257. who calls .Resume.
  258. If a thread is supposed to be suspended (from outside its own path of
  259. execution) on a system where the symbol LINUX is defined, two things
  260. are possible.
  261. 1) the system has the LinuxThreads pthread implementation
  262. 2) the system has NPTL as the pthread implementation.
  263. In the first case, each thread is a process on its own, which as far as
  264. know actually violates posix with respect to signal handling.
  265. But we can detect this case, because getpid(2) will
  266. return a different PID for each thread. In that case, sending SIGSTOP
  267. to the PID associated with a thread will actually stop that thread
  268. only.
  269. In the second case, this is not possible. But getpid(2) returns the same
  270. PID across all threads, which is detected, and TThread.Suspend() does
  271. nothing in that case. This should probably be changed, but I know of
  272. no way to suspend a thread when using NPTL.
  273. If the symbol LINUX is not defined, then the unimplemented
  274. function SuspendThread is called.
  275. Johannes Berg <[email protected]>, Sunday, November 16 2003
  276. }
  277. // ========== semaphore stuff ==========
  278. {
  279. I don't like this. It eats up 2 filedescriptors for each thread,
  280. and those are a limited resource. If you have a server programm
  281. handling client connections (one per thread) it will not be able
  282. to handle many if we use 2 fds already for internal structures.
  283. However, right now I don't see a better option unless some sem_*
  284. functions are added to systhrds.
  285. I encapsulated all used functions here to make it easier to
  286. change them completely.
  287. }
  288. function SemaphoreInit: Pointer;
  289. begin
  290. SemaphoreInit := GetMem(SizeOf(TFilDes));
  291. fppipe(PFilDes(SemaphoreInit)^);
  292. end;
  293. procedure SemaphoreWait(const FSem: Pointer);
  294. var
  295. b: byte;
  296. begin
  297. fpread(PFilDes(FSem)^[0], b, 1);
  298. end;
  299. procedure SemaphorePost(const FSem: Pointer);
  300. begin
  301. fpwrite(PFilDes(FSem)^[1], #0, 1);
  302. end;
  303. procedure SemaphoreDestroy(const FSem: Pointer);
  304. begin
  305. fpclose(PFilDes(FSem)^[0]);
  306. fpclose(PFilDes(FSem)^[1]);
  307. FreeMemory(FSem);
  308. end;
  309. // =========== semaphore end ===========
  310. var
  311. ThreadsInited: boolean = false;
  312. {$IFDEF LINUX}
  313. GMainPID: LongInt = 0;
  314. {$ENDIF}
  315. const
  316. // stupid, considering its not even implemented...
  317. Priorities: array [TThreadPriority] of Integer =
  318. (-20,-19,-10,0,9,18,19);
  319. procedure InitThreads;
  320. begin
  321. if not ThreadsInited then begin
  322. ThreadsInited := true;
  323. {$IFDEF LINUX}
  324. GMainPid := fpgetpid();
  325. {$ENDIF}
  326. end;
  327. end;
  328. procedure DoneThreads;
  329. begin
  330. ThreadsInited := false;
  331. end;
  332. { ok, so this is a hack, but it works nicely. Just never use
  333. a multiline argument with WRITE_DEBUG! }
  334. {$MACRO ON}
  335. {$IFDEF DEBUG_MT}
  336. {$define WRITE_DEBUG := writeln} // actually write something
  337. {$ELSE}
  338. {$define WRITE_DEBUG := //} // just comment out those lines
  339. {$ENDIF}
  340. function ThreadFunc(parameter: Pointer): LongInt; cdecl;
  341. var
  342. LThread: TThread;
  343. c: char;
  344. begin
  345. WRITE_DEBUG('ThreadFunc is here...');
  346. LThread := TThread(parameter);
  347. {$IFDEF LINUX}
  348. // save the PID of the "thread"
  349. // this is different from the PID of the main thread if
  350. // the LinuxThreads implementation is used
  351. LThread.FPid := fpgetpid();
  352. {$ENDIF}
  353. WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
  354. try
  355. if LThread.FInitialSuspended then begin
  356. SemaphoreWait(LThread.FSem);
  357. if not LThread.FInitialSuspended then begin
  358. WRITE_DEBUG('going into LThread.Execute');
  359. LThread.Execute;
  360. end;
  361. end else begin
  362. WRITE_DEBUG('going into LThread.Execute');
  363. LThread.Execute;
  364. end;
  365. except
  366. on e: exception do begin
  367. WRITE_DEBUG('got exception: ',e.message);
  368. LThread.FFatalException := TObject(AcquireExceptionObject);
  369. // not sure if we should really do this...
  370. // but .Destroy was called, so why not try FreeOnTerminate?
  371. if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
  372. end;
  373. end;
  374. WRITE_DEBUG('thread done running');
  375. Result := LThread.FReturnValue;
  376. WRITE_DEBUG('Result is ',Result);
  377. LThread.FFinished := True;
  378. LThread.DoTerminate;
  379. if LThread.FreeOnTerminate then begin
  380. WRITE_DEBUG('Thread should be freed');
  381. LThread.Free;
  382. WRITE_DEBUG('Thread freed');
  383. end;
  384. WRITE_DEBUG('thread func exiting');
  385. end;
  386. { TThread }
  387. constructor TThread.Create(CreateSuspended: Boolean);
  388. begin
  389. // lets just hope that the user doesn't create a thread
  390. // via BeginThread and creates the first TThread Object in there!
  391. InitThreads;
  392. inherited Create;
  393. FSem := SemaphoreInit;
  394. FSuspended := CreateSuspended;
  395. FSuspendedExternal := false;
  396. FInitialSuspended := CreateSuspended;
  397. FFatalException := nil;
  398. WRITE_DEBUG('creating thread, self = ',longint(self));
  399. FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID);
  400. WRITE_DEBUG('TThread.Create done');
  401. end;
  402. destructor TThread.Destroy;
  403. begin
  404. if FThreadID = GetCurrentThreadID then begin
  405. raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
  406. end;
  407. // if someone calls .Free on a thread with
  408. // FreeOnTerminate, then don't crash!
  409. FFreeOnTerminate := false;
  410. if not FFinished and not FSuspended then begin
  411. Terminate;
  412. WaitFor;
  413. end;
  414. if (FInitialSuspended) then begin
  415. // thread was created suspended but never woken up.
  416. SemaphorePost(FSem);
  417. WaitFor;
  418. end;
  419. FFatalException.Free;
  420. FFatalException := nil;
  421. SemaphoreDestroy(FSem);
  422. inherited Destroy;
  423. end;
  424. procedure TThread.SetSuspended(Value: Boolean);
  425. begin
  426. if Value <> FSuspended then
  427. if Value then
  428. Suspend
  429. else
  430. Resume;
  431. end;
  432. procedure TThread.Suspend;
  433. begin
  434. if not FSuspended then begin
  435. if FThreadID = GetCurrentThreadID then begin
  436. FSuspended := true;
  437. SemaphoreWait(FSem);
  438. end else begin
  439. FSuspendedExternal := true;
  440. {$IFDEF LINUX}
  441. // naughty hack if the user doesn't have Linux with NPTL...
  442. // in that case, the PID of threads will not be identical
  443. // to the other threads, which means that our thread is a normal
  444. // process that we can suspend via SIGSTOP...
  445. // this violates POSIX, but is the way it works on the
  446. // LinuxThreads pthread implementation. Not with NPTL, but in that case
  447. // getpid(2) also behaves properly and returns the same PID for
  448. // all threads. Thats actually (FINALLY!) native thread support :-)
  449. if FPid <> GMainPID then begin
  450. FSuspended := true;
  451. fpkill(FPid, SIGSTOP);
  452. end;
  453. {$ELSE}
  454. SuspendThread(FHandle);
  455. {$ENDIF}
  456. end;
  457. end;
  458. end;
  459. procedure TThread.Resume;
  460. begin
  461. if (not FSuspendedExternal) then begin
  462. if FSuspended then begin
  463. SemaphorePost(FSem);
  464. FInitialSuspended := false;
  465. FSuspended := False;
  466. end;
  467. end else begin
  468. {$IFDEF LINUX}
  469. // see .Suspend
  470. if FPid <> GMainPID then begin
  471. fpkill(FPid, SIGCONT);
  472. FSuspended := False;
  473. end;
  474. {$ELSE}
  475. ResumeThread(FHandle);
  476. {$ENDIF}
  477. FSuspendedExternal := false;
  478. end;
  479. end;
  480. procedure TThread.Terminate;
  481. begin
  482. FTerminated := True;
  483. end;
  484. function TThread.WaitFor: Integer;
  485. begin
  486. WRITE_DEBUG('waiting for thread ',FHandle);
  487. WaitFor := WaitForThreadTerminate(FHandle, 0);
  488. WRITE_DEBUG('thread terminated');
  489. end;
  490. procedure TThread.CallOnTerminate;
  491. begin
  492. // no need to check if FOnTerminate <> nil, because
  493. // thats already done in DoTerminate
  494. FOnTerminate(self);
  495. end;
  496. procedure TThread.DoTerminate;
  497. begin
  498. if Assigned(FOnTerminate) then
  499. Synchronize(@CallOnTerminate);
  500. end;
  501. function TThread.GetPriority: TThreadPriority;
  502. var
  503. P: Integer;
  504. I: TThreadPriority;
  505. begin
  506. P := ThreadGetPriority(FHandle);
  507. Result := tpNormal;
  508. for I := Low(TThreadPriority) to High(TThreadPriority) do
  509. if Priorities[I] = P then
  510. Result := I;
  511. end;
  512. procedure TThread.Synchronize(Method: TThreadMethod);
  513. begin
  514. {$TODO someone with more clue of the GUI stuff will have to do this}
  515. end;
  516. procedure TThread.SetPriority(Value: TThreadPriority);
  517. begin
  518. ThreadSetPriority(FHandle, Priorities[Value]);
  519. end;
  520. {$ENDIF}
  521. {
  522. $Log$
  523. Revision 1.1 2004-01-04 20:05:38 jonas
  524. * first working version of the Darwin/Mac OS X (for PowerPC) RTL
  525. Several non-essential units are still missing, but make cycle works
  526. Revision 1.7 2003/11/22 11:04:08 marco
  527. * Johill: suspend fix
  528. Revision 1.6 2003/11/19 10:12:02 marco
  529. * more cleanups
  530. Revision 1.5 2003/11/17 10:05:51 marco
  531. * threads for FreeBSD. Not working tho
  532. Revision 1.4 2003/11/17 08:27:49 marco
  533. * pthreads based ttread from Johannes Berg
  534. Revision 1.3 2003/11/10 16:54:28 marco
  535. * new oldlinux unit. 1_0 defines killed in some former FCL parts.
  536. Revision 1.2 2003/11/03 09:42:28 marco
  537. * Peter's Cardinal<->Longint fixes patch
  538. Revision 1.1 2003/10/06 21:01:06 peter
  539. * moved classes unit to rtl
  540. Revision 1.9 2003/10/06 17:06:55 florian
  541. * applied Johannes Berg's patch for exception handling in threads
  542. Revision 1.8 2003/09/20 15:10:30 marco
  543. * small fixes. fcl now compiles
  544. Revision 1.7 2002/12/18 20:44:36 peter
  545. * use fillchar to clear sigset
  546. Revision 1.6 2002/09/07 15:15:27 peter
  547. * old logs removed and tabs fixed
  548. }