athreads.pp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2015 by Karoly Balogh,
  4. member of the Free Pascal development team.
  5. native threadmanager implementation for Amiga-like systems
  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. {$mode objfpc}
  13. unit athreads;
  14. interface
  15. procedure SetAThreadBaseName(s: String);
  16. implementation
  17. { enable this to compile athreads easily outside the RTL }
  18. {.$DEFINE ATHREADS_STANDALONE}
  19. {$IFDEF ATHREADS_STANDALONE}
  20. uses
  21. exec, amigados, utility;
  22. {$ELSE}
  23. { * Include required system specific includes * }
  24. {$include execd.inc}
  25. {$include execf.inc}
  26. {$include timerd.inc}
  27. {$include doslibd.inc}
  28. {$include doslibf.inc}
  29. {$ENDIF}
  30. const
  31. threadvarblocksize : dword = 0;
  32. var
  33. SubThreadBaseName: String = 'FPC Subthread';
  34. {.$define DEBUG_MT}
  35. {.$define DEBUG_AMIEVENT}
  36. type
  37. TThreadOperation = ( toNone, toStart, toResume, toExit );
  38. type
  39. PThreadMsg = ^TThreadMsg;
  40. PThreadInfo = ^TThreadInfo;
  41. TThreadInfo = record
  42. threadVars: Pointer; { have threadvars ptr as first field, so no offset is needed to access it (faster) }
  43. threadVarsSize: DWord; { size of the allocated threadvars block }
  44. nextThread: PThreadInfo; { threadinfos are a linked list, using this field }
  45. threadPtr: PProcess; { our thread pointer, as returned by CreateNewProc(). invalid after exited field is true! }
  46. threadID: TThreadID; { thread Unique ID }
  47. stackLen: PtrUInt; { stack size the thread was construced with }
  48. exitCode: Pointer; { exitcode after the process has exited }
  49. f: TThreadFunc; { ThreadFunc function pointer }
  50. p: Pointer; { ThreadFunc argument }
  51. flags: dword; { Flags this thread were created with }
  52. num: longint; { This was the "num"th thread to created }
  53. mainthread: boolean; { true if this is our main thread }
  54. exited: boolean; { true if the thread has exited, and can be cleaned up }
  55. startSuspended: boolean; { true if the thread was started suspended, and not resumed yet }
  56. suspended: boolean; { true if the thread is currently suspended }
  57. mutex: TSignalSemaphore; { thread's mutex. locked during the thread's life. }
  58. name: String; { Thread's name }
  59. end;
  60. TThreadMsg = record
  61. tm_MsgNode : TMessage;
  62. tm_ThreadInfo: PThreadInfo;
  63. tm_Operation : TThreadOperation;
  64. end;
  65. var
  66. AThreadManager: TThreadManager;
  67. AThreadList: PThreadInfo;
  68. AThreadListLen: LongInt;
  69. AThreadNum: LongInt;
  70. AThreadListSemaphore: TSignalSemaphore;
  71. { Simple IntToStr() replacement which works with ShortStrings }
  72. function IToStr(const i: LongInt): String;
  73. begin
  74. Str(I,result);
  75. end;
  76. {$IFDEF DEBUG_MT}
  77. function IToHStr(const i: LongInt): String;
  78. begin
  79. result:=HexStr(Pointer(i));
  80. end;
  81. {$ENDIF}
  82. { Function to add a thread to the running threads list }
  83. procedure AddToThreadList(var l: PThreadInfo; ti: PThreadInfo);
  84. var
  85. p : PThreadInfo;
  86. inList: Boolean;
  87. begin
  88. inList:=False;
  89. ObtainSemaphore(@AThreadListSemaphore);
  90. if l = nil then
  91. { if the list is not yet allocated, the newly added
  92. threadinfo will be the first item }
  93. l:=ti
  94. else
  95. begin
  96. { otherwise, look for the last item and append }
  97. p:=l;
  98. while (p^.nextThread<>nil) do p:=p^.nextThread;
  99. p^.nextThread:=ti;
  100. end;
  101. inc(AThreadNum);
  102. ti^.num:=AThreadNum;
  103. inc(AThreadListLen);
  104. {$IFDEF DEBUG_MT}
  105. SysDebugLn('FPC AThreads: thread ID:'+IToHStr(ti^.threadID)+' added, now '+IToStr(AThreadListLen)+' thread(s) in list.');
  106. {$ENDIF}
  107. ReleaseSemaphore(@AThreadListSemaphore);
  108. end;
  109. { Function to remove a thread from running threads list }
  110. function RemoveFromThreadList(var l: PThreadInfo; threadID: TThreadID): boolean;
  111. var
  112. p : PThreadInfo;
  113. pprev : PThreadInfo;
  114. inList : Boolean;
  115. tmpNext: PThreadInfo;
  116. tmpInfo: PThreadInfo;
  117. begin
  118. inList:=False;
  119. if l=nil then
  120. begin
  121. RemoveFromThreadList:=inList;
  122. exit;
  123. end;
  124. ObtainSemaphore(@AThreadListSemaphore);
  125. p:=l;
  126. pprev:=nil;
  127. while (p <> nil) and (p^.threadID <> threadID) do
  128. begin
  129. pprev:=p;
  130. p:=p^.nextThread;
  131. end;
  132. if p <> nil then
  133. begin
  134. tmpNext:=p^.nextThread;
  135. if not p^.mainthread and p^.exited then
  136. begin
  137. {$IFDEF DEBUG_MT}
  138. SysDebugLn('FPC AThreads: Releasing resources for thread ID:'+IToHStr(threadID));
  139. if (p^.threadVars <> nil) or (p^.threadVarsSize <> 0) then
  140. SysDebugLn('FPC AThreads: WARNING, threadvars area wasn''t properly freed!'+IToHStr(threadID));
  141. {$ENDIF}
  142. dispose(p);
  143. if pprev <> nil then
  144. pprev^.nextThread:=tmpNext;
  145. Dec(AThreadListLen);
  146. end
  147. else
  148. begin
  149. {$IFDEF DEBUG_MT}
  150. SysDebugLn('FPC AThreads: Error! Attempt to remove threadID, which is the mainthread or not exited:'+IToHStr(threadID));
  151. {$ENDIF}
  152. inList:=false;
  153. end;
  154. end
  155. {$IFDEF DEBUG_MT}
  156. else
  157. SysDebugLn('FPC AThreads: Error! Attempt to remove threadID, which is not in list:'+IToHstr(threadID))
  158. {$ENDIF}
  159. ;
  160. ReleaseSemaphore(@AThreadListSemaphore);
  161. RemoveFromThreadList:=inList;
  162. end;
  163. { Function to return a function's ThreadInfo based on the threadID }
  164. function GetThreadInfo(var l: PThreadInfo; threadID: TThreadID): PThreadInfo;
  165. var
  166. p : PThreadInfo;
  167. inList: Boolean;
  168. begin
  169. inList:=False;
  170. GetThreadInfo:=nil;
  171. if l = nil then
  172. exit;
  173. ObtainSemaphoreShared(@AThreadListSemaphore);
  174. p:=l;
  175. while (p <> nil) and (p^.threadID <> threadID) do
  176. p:=p^.nextThread;
  177. GetThreadInfo:=p;
  178. ReleaseSemaphore(@AThreadListSemaphore);
  179. end;
  180. { Get current thread's ThreadInfo structure }
  181. function GetCurrentThreadInfo: PThreadInfo;
  182. begin
  183. result:=PThreadInfo(PProcess(FindTask(nil))^.pr_Task.tc_UserData);
  184. end;
  185. { Returns the number of threads still not exited in our threadlist }
  186. function CountRunningThreads(var l: PThreadInfo): LongInt;
  187. var
  188. p: PThreadInfo;
  189. begin
  190. CountRunningThreads:=0;
  191. ObtainSemaphoreShared(@AThreadListSemaphore);
  192. p:=l;
  193. while p <> nil do
  194. begin
  195. inc(CountRunningThreads,ord(not p^.exited));
  196. p:=p^.nextThread;
  197. end;
  198. ReleaseSemaphore(@AThreadListSemaphore);
  199. end;
  200. { Helper function for IPC }
  201. procedure SendMessageToThread(var threadMsg: TThreadMsg; p: PThreadInfo; const op: TThreadOperation; waitReply: boolean);
  202. var
  203. replyPort: PMsgPort;
  204. begin
  205. replyPort:=@PProcess(FindTask(nil))^.pr_MsgPort;
  206. FillChar(threadMsg,sizeof(threadMsg),0);
  207. with threadMsg do
  208. begin
  209. with tm_MsgNode do
  210. begin
  211. mn_Node.ln_Type:=NT_MESSAGE;
  212. mn_Length:=SizeOf(TThreadMsg);
  213. if waitReply then
  214. mn_ReplyPort:=replyPort
  215. else
  216. mn_ReplyPort:=nil;
  217. end;
  218. tm_ThreadInfo:=p;
  219. tm_Operation:=op;
  220. end;
  221. PutMsg(@p^.threadPtr^.pr_MsgPort,@threadMsg);
  222. if waitReply then
  223. begin
  224. WaitPort(replyPort);
  225. GetMsg(replyPort);
  226. end;
  227. end;
  228. procedure SetAThreadBaseName(s: String);
  229. begin
  230. ObtainSemaphore(@AThreadListSemaphore);
  231. SubThreadBaseName:=s;
  232. ReleaseSemaphore(@AThreadListSemaphore);
  233. end;
  234. function GetAThreadBaseName: String;
  235. begin
  236. ObtainSemaphoreShared(@AThreadListSemaphore);
  237. GetAThreadBaseName:=SubThreadBaseName;
  238. ReleaseSemaphore(@AThreadListSemaphore);
  239. end;
  240. procedure AInitThreadvar(var offset : dword;size : dword);
  241. begin
  242. {$IFDEF DEBUG_MT}
  243. {SysDebugLn('FPC AThreads: InitThreadvar');}
  244. {$ENDIF}
  245. offset:=threadvarblocksize;
  246. inc(threadvarblocksize,size);
  247. end;
  248. function ARelocateThreadvar(offset : dword) : pointer;
  249. var
  250. p: PThreadInfo;
  251. begin
  252. {$IFDEF DEBUG_MT}
  253. {SysDebugLn('FPC AThreads: RelocateThreadvar');}
  254. {$ENDIF}
  255. p:=GetCurrentThreadInfo;
  256. if (p <> nil) and (p^.threadVars <> nil) then
  257. result:=p^.threadVars + Offset
  258. else
  259. result:=nil;
  260. end;
  261. procedure AAllocateThreadVars;
  262. var
  263. p: PThreadInfo;
  264. begin
  265. { we've to allocate the memory from system }
  266. { because the FPC heap management uses }
  267. { exceptions which use threadvars but }
  268. { these aren't allocated yet ... }
  269. { allocate room on the heap for the thread vars }
  270. p:=GetCurrentThreadInfo;
  271. if p <> nil then
  272. begin
  273. {$ifdef DEBUG_MT}
  274. SysDebugLn('FPC AThreads: Allocating threadvars, ID:'+IToHStr(p^.threadID));
  275. {$endif}
  276. {$ifdef AMIGA}
  277. ObtainSemaphore(ASYS_heapSemaphore);
  278. {$endif}
  279. p^.threadVars:=AllocPooled(ASYS_heapPool,threadvarblocksize);
  280. if p^.threadVars = nil then
  281. SysDebugLn('FPC AThreads: Failed to allocate threadvar memory!')
  282. else
  283. begin
  284. p^.threadVarsSize:=threadvarblocksize;
  285. FillChar(p^.threadVars^,threadvarblocksize,0);
  286. end;
  287. {$ifdef AMIGA}
  288. ReleaseSemaphore(ASYS_heapSemaphore);
  289. {$endif}
  290. end
  291. else
  292. begin
  293. {$ifdef DEBUG_MT}
  294. SysDebugLn('FPC AThreads: AllocateThreadVars: tc_UserData of this process was nil!')
  295. {$endif}
  296. end;
  297. end;
  298. procedure AReleaseThreadVars;
  299. var
  300. p: PThreadInfo;
  301. begin
  302. p:=GetCurrentThreadInfo;
  303. if (p <> nil) and (p^.threadVars <> nil) then
  304. begin
  305. {$ifdef DEBUG_MT}
  306. SysDebugLn('FPC AThreads: Releasing threadvars, ID:'+IToHStr(p^.threadID));
  307. {$endif}
  308. {$ifdef AMIGA}
  309. ObtainSemaphore(ASYS_heapSemaphore);
  310. {$endif}
  311. FreePooled(ASYS_heapPool,p^.threadVars,p^.threadVarsSize);
  312. p^.threadVars:=nil;
  313. p^.threadVarsSize:=0;
  314. {$ifdef AMIGA}
  315. ReleaseSemaphore(ASYS_heapSemaphore);
  316. {$endif}
  317. end
  318. else
  319. begin
  320. {$ifdef DEBUG_MT}
  321. SysDebugLn('FPC AThreads: ReleaseThreadVars: tc_UserData or threadVars area of this process was nil!')
  322. {$endif}
  323. end;
  324. end;
  325. procedure InitAThreading;
  326. var
  327. threadInfo: PThreadInfo;
  328. p: PProcess;
  329. begin
  330. if (InterLockedExchange(longint(IsMultiThread),ord(true)) = 0) then
  331. begin
  332. { We're still running in single thread mode, setup the TLS }
  333. {$ifdef DEBUG_MT}
  334. SysDebugLn('FPC AThreads: Entering multithreaded mode...');
  335. {$endif}
  336. p:=PProcess(FindTask(nil));
  337. new(threadInfo);
  338. FillChar(threadInfo^,sizeof(TThreadInfo),0);
  339. p^.pr_Task.tc_UserData:=threadInfo;
  340. threadInfo^.mainThread:=true;
  341. InitSemaphore(@threadInfo^.mutex);
  342. ObtainSemaphore(@threadInfo^.mutex);
  343. threadInfo^.threadPtr:=p;
  344. threadInfo^.threadID:=TThreadID(threadInfo);
  345. InitThreadVars(@ARelocateThreadvar);
  346. AddToThreadList(AThreadList,threadInfo);
  347. end;
  348. end;
  349. procedure ThreadFunc; cdecl;
  350. var
  351. thisThread: PProcess;
  352. threadMsg: PThreadMsg;
  353. resumeMsg: PThreadMsg;
  354. exitSuspend: boolean; // true if we have to exit instead of resuming
  355. threadInfo: PThreadInfo;
  356. begin
  357. thisThread:=PProcess(FindTask(nil));
  358. { wait for our start message to arrive, then fetch it }
  359. WaitPort(@thisThread^.pr_MsgPort);
  360. threadMsg:=PThreadMsg(GetMsg(@thisThread^.pr_MsgPort));
  361. { fetch existing threadinfo from the start message, and set
  362. it to tc_userData, so we can proceed with threadvars }
  363. threadInfo:=threadMsg^.tm_ThreadInfo;
  364. thisThread^.pr_Task.tc_userData:=threadInfo;
  365. {$ifdef DEBUG_MT}
  366. SysDebugLn('FPC AThreads: Entering subthread function, ID:'+hexStr(threadInfo));
  367. {$endif}
  368. { Obtain the threads' mutex, used for exit sync }
  369. ObtainSemaphore(@threadInfo^.mutex);
  370. { Allocate local thread vars, this must be the first thing,
  371. because the exception management and io depends on threadvars }
  372. AAllocateThreadVars;
  373. { Rename the thread into something sensible }
  374. if threadInfo^.name <> '' then
  375. begin
  376. {$ifdef DEBUG_MT}
  377. { this line can't be before threadvar allocation }
  378. SysDebugLn('FPC AThreads: Renaming thread ID:'+hexStr(threadInfo)+' to '+threadInfo^.name);
  379. {$endif}
  380. thisThread^.pr_Task.tc_Node.ln_Name:=PChar(@threadInfo^.name[1]);
  381. end;
  382. { Reply the message, so the calling thread could continue }
  383. { note that threadMsg was allocated on the caller's task, so }
  384. { it will be invalid below this point }
  385. ReplyMsg(PMessage(threadMsg));
  386. { if creating a suspended thread, wait for the wakeup message to arrive }
  387. { then check if we actually have to resume, or exit }
  388. exitSuspend:=false;
  389. if threadInfo^.startSuspended then
  390. begin
  391. {$ifdef DEBUG_MT}
  392. SysDebugLn('FPC AThreads: Suspending subthread on entry, ID:'+hexStr(threadInfo));
  393. {$endif}
  394. WaitPort(@thisThread^.pr_MsgPort);
  395. resumeMsg:=PThreadMsg(GetMsg(@thisThread^.pr_MsgPort));
  396. exitSuspend:=resumeMsg^.tm_Operation <> toResume;
  397. threadInfo^.startSuspended:=false;
  398. ReplyMsg(PMessage(resumeMsg));
  399. {$ifdef DEBUG_MT}
  400. SysDebugLn('FPC AThreads: Resuming subthread on entry, ID:'+hexStr(threadInfo)+', resumed only to exit: '+IToStr(ord(exitSuspend)));
  401. {$endif}
  402. end;
  403. { Finally, call the user code }
  404. if not exitSuspend then
  405. begin
  406. InitThread(threadInfo^.stackLen);
  407. DoThreadInitProcChain;
  408. threadInfo^.exitCode:=Pointer(threadInfo^.f(threadInfo^.p));
  409. DoThreadExitProcChain;
  410. DoneThread;
  411. end;
  412. {$ifdef DEBUG_MT}
  413. SysDebugLn('FPC AThreads: Exiting Subthread function, ID:'+hexStr(threadInfo));
  414. {$endif}
  415. Forbid();
  416. threadInfo^.exited:=true;
  417. { Finally, Release our exit mutex. }
  418. ReleaseSemaphore(@threadInfo^.mutex);
  419. end;
  420. function CreateNewProcess(const Tags : Array Of PtrUInt) : PProcess;
  421. begin
  422. result:=CreateNewProc(@Tags[0]);
  423. end;
  424. function ABeginThread(sa : Pointer;stacksize : PtrUInt;
  425. ThreadFunction : tthreadfunc;p : pointer;
  426. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  427. var
  428. threadInfo: PThreadInfo;
  429. threadMsg: TThreadMsg;
  430. threadName: String;
  431. subThread: PProcess;
  432. begin
  433. ABeginThread:=TThreadID(0);
  434. {$ifdef DEBUG_MT}
  435. SysDebugLn('FPC AThreads: Creating new thread...');
  436. {$endif DEBUG_MT}
  437. { Initialize multithreading if not done }
  438. if not IsMultiThread then
  439. InitAThreading;
  440. { the only way to pass data to the newly created thread
  441. in a MT safe way, is to use the heap }
  442. new(threadInfo);
  443. FillChar(threadInfo^,sizeof(TThreadInfo),0);
  444. InitSemaphore(@threadInfo^.mutex);
  445. threadInfo^.f:=ThreadFunction;
  446. threadInfo^.p:=p;
  447. if (creationFlags and STACK_SIZE_PARAM_IS_A_RESERVATION) > 0 then
  448. threadInfo^.stackLen:=stacksize
  449. else
  450. threadInfo^.stackLen:=System.StackLength; { inherit parent's stack size }
  451. threadInfo^.startSuspended:=(creationFlags and CREATE_SUSPENDED) > 0;
  452. {$ifdef DEBUG_MT}
  453. SysDebugLn('FPC AThreads: Starting new thread... Stack size: '+IToStr(threadInfo^.stackLen));
  454. {$endif}
  455. subThread:=CreateNewProcess([NP_Entry,PtrUInt(@ThreadFunc),
  456. {$IFDEF MORPHOS}
  457. NP_CodeType,CODETYPE_PPC,
  458. NP_PPCStackSize,threadInfo^.stacklen,
  459. {$ELSE}
  460. NP_StackSize,threadInfo^.stacklen,
  461. {$ENDIF}
  462. TAG_DONE]);
  463. if subThread = nil then
  464. begin
  465. {$ifdef DEBUG_MT}
  466. SysDebugLn('FPC AThreads: Failed to start the subthread!');
  467. {$endif}
  468. exit;
  469. end;
  470. ThreadID:=TThreadID(threadInfo);
  471. threadInfo^.threadPtr:=subThread;
  472. threadInfo^.threadID:=ThreadID;
  473. AddToThreadList(AThreadList,threadInfo);
  474. { the thread should be started, and waiting for our start message, so send it }
  475. {$ifdef DEBUG_MT}
  476. SysDebugLn('FPC AThreads: Sending start message to subthread and waiting for reply, ID:'+IToHStr(threadID));
  477. {$endif}
  478. { AddToThreadList assigned us a number, so use it to name the thread }
  479. threadInfo^.name:=GetAThreadBaseName+' #'+IToStr(threadInfo^.num);
  480. SendMessageToThread(threadMsg,threadInfo,toStart,true);
  481. ABeginThread:=ThreadId;
  482. {$ifdef DEBUG_MT}
  483. SysDebugLn('FPC AThreads: Thread created successfully, ID:'+IToHStr(threadID));
  484. {$endif}
  485. end;
  486. procedure AEndThread(ExitCode : DWord);
  487. begin
  488. { Do not call DoneThread here. It will be called by the threadfunction, when it exits. }
  489. end;
  490. function ASuspendThread (threadHandle : TThreadID) : dword;
  491. var
  492. p: PThreadInfo;
  493. m: PThreadMsg;
  494. begin
  495. ASuspendThread:=0;
  496. if GetCurrentThreadID = threadHandle then
  497. begin
  498. p:=GetThreadInfo(AThreadList,threadHandle);
  499. if p <> nil then
  500. begin
  501. p^.suspended:=true;
  502. while p^.suspended do
  503. begin
  504. WaitPort(@p^.threadPtr^.pr_MsgPort);
  505. m:=PThreadMsg(GetMsg(@p^.threadPtr^.pr_MsgPort));
  506. if m^.tm_Operation = toResume then
  507. p^.suspended:=false
  508. else
  509. {$ifdef DEBUG_MT}
  510. SysDebugLn('FPC AThreads: Got message during suspend, but it wasn''t toResume! ID:'+IToHStr(threadHandle))
  511. {$endif}
  512. ;
  513. ReplyMsg(PMessage(m));
  514. end;
  515. end;
  516. end
  517. else
  518. begin
  519. {$ifdef DEBUG_MT}
  520. SysDebugLn('FPC AThreads: SuspendThread called for ID:'+IToHStr(threadHandle)+' which is not the current thread!');
  521. {$endif}
  522. result:=dword(-1);
  523. end;
  524. end;
  525. function AResumeThread (threadHandle : TThreadID) : dword;
  526. var
  527. m: TThreadMsg;
  528. p: PThreadInfo;
  529. begin
  530. AResumeThread:=0;
  531. Forbid();
  532. p:=GetThreadInfo(AThreadList,threadHandle);
  533. if (p <> nil) and (p^.suspended or p^.startSuspended) then
  534. begin
  535. {$ifdef DEBUG_MT}
  536. SysDebugLn('FPC AThreads: Waiting for thread to resume, ID:'+IToHStr(threadHandle));
  537. {$endif}
  538. { WaitPort in SendMessageToThread will break the Forbid() state... }
  539. SendMessageToThread(m,p,toResume,true);
  540. AResumeThread:=0;
  541. end
  542. else
  543. begin
  544. {$ifdef DEBUG_MT}
  545. SysDebugLn('FPC AThreads: Error, attempt to resume a non-suspended thread, or invalid thread ID:'+IToHStr(threadHandle));
  546. {$endif}
  547. AResumeThread:=dword(-1);
  548. end;
  549. Permit();
  550. end;
  551. procedure AThreadSwitch; {give time to other threads}
  552. begin
  553. { On Unix, this calls sched_yield();
  554. Harry 'Piru' Sintonen recommended to emulate this on Amiga systems with
  555. exec/Forbid-exec/Permit pair which is pretty fast to execute and will
  556. trigger a rescheduling.
  557. Another idea by Frank Mariak was to use exec/SetTaskPri() with the same
  558. priority }
  559. Forbid();
  560. Permit();
  561. end;
  562. function AKillThread (threadHandle : TThreadID) : dword;
  563. begin
  564. {$ifdef DEBUG_MT}
  565. SysDebugLn('FPC AThreads: unsupported operation: KillThread called for ID:'+IToHStr(threadHandle));
  566. {$endif}
  567. // cannot be properly supported on Amiga
  568. AKillThread:=dword(-1);
  569. end;
  570. function ACloseThread (threadHandle : TThreadID) : dword;
  571. begin
  572. {$WARNING The return value here seems to be undocumented}
  573. RemoveFromThreadList(AThreadList, threadHandle);
  574. result:=0;
  575. end;
  576. function AWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  577. var
  578. p: PThreadInfo;
  579. m: TThreadMsg;
  580. begin
  581. {.$WARNING Support for timeout argument is not implemented}
  582. { But since CThreads uses pthread_join, which has also no timeout,
  583. I don't think this is a big issue. (KB) }
  584. AWaitForThreadTerminate:=0;
  585. Forbid();
  586. p:=GetThreadInfo(AThreadList,threadHandle);
  587. if (p <> nil) then
  588. begin
  589. if not p^.exited then
  590. begin
  591. {$ifdef DEBUG_MT}
  592. SysDebugLn('FPC AThreads: Waiting for thread to exit, ID:'+IToHStr(threadHandle));
  593. {$endif}
  594. { WaitPort in SendMessageToThread will break the Forbid() state... }
  595. if p^.startSuspended then
  596. begin
  597. SendMessageToThread(m,p,toExit,true);
  598. {$ifdef DEBUG_MT}
  599. SysDebugLn('FPC AThreads: Signaled start-suspended thread to exit, ID:'+IToHStr(threadHandle));
  600. {$endif}
  601. end;
  602. { Wait for the thread to exit... }
  603. Permit();
  604. ObtainSemaphore(@p^.mutex);
  605. ReleaseSemaphore(@p^.mutex);
  606. Forbid();
  607. end
  608. else
  609. {$ifdef DEBUG_MT}
  610. SysDebugLn('FPC AThreads: Thread already exited, ID:'+IToHStr(threadHandle));
  611. {$endif}
  612. AWaitForThreadTerminate:=DWord(p^.exitCode);
  613. end
  614. else
  615. begin
  616. {$ifdef DEBUG_MT}
  617. SysDebugLn('FPC AThreads: Error, attempt to wait for invalid thread ID to exit, ID:'+IToHStr(threadHandle));
  618. {$endif}
  619. AWaitForThreadTerminate:=dword(-1); { Return non-zero code on error. }
  620. end;
  621. Permit();
  622. end;
  623. function AThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  624. begin
  625. {$Warning ThreadSetPriority needs to be implemented}
  626. result:=false;
  627. end;
  628. function AThreadGetPriority (threadHandle : TThreadID): Integer;
  629. begin
  630. {$Warning ThreadGetPriority needs to be implemented}
  631. result:=0;
  632. end;
  633. function AGetCurrentThreadId : TThreadID;
  634. begin
  635. AGetCurrentThreadId := TThreadID(GetCurrentThreadInfo);
  636. end;
  637. Type PINTRTLEvent = ^TINTRTLEvent;
  638. TINTRTLEvent = record
  639. isset: boolean;
  640. end;
  641. Function intRTLEventCreate: PRTLEvent;
  642. var p:pintrtlevent;
  643. begin
  644. new(p);
  645. result:=PRTLEVENT(p);
  646. end;
  647. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  648. var p:pintrtlevent;
  649. begin
  650. p:=pintrtlevent(aevent);
  651. dispose(p);
  652. end;
  653. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  654. var p:pintrtlevent;
  655. begin
  656. p:=pintrtlevent(aevent);
  657. p^.isset:=true;
  658. end;
  659. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  660. var p:pintrtlevent;
  661. begin
  662. p:=pintrtlevent(aevent);
  663. p^.isset:=false;
  664. end;
  665. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  666. var p:pintrtlevent;
  667. begin
  668. p:=pintrtlevent(aevent);
  669. p^.isset:=false;
  670. end;
  671. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  672. var
  673. p : pintrtlevent;
  674. begin
  675. p:=pintrtlevent(aevent);
  676. end;
  677. procedure AInitCriticalSection(var CS);
  678. begin
  679. {$IFDEF DEBUG_MT}
  680. SysDebugLn('FPC AThreads: InitCriticalSection '+hexStr(@CS));
  681. {$ENDIF}
  682. InitSemaphore(PSignalSemaphore(@CS));
  683. end;
  684. procedure AEnterCriticalSection(var CS);
  685. begin
  686. {$IFDEF DEBUG_MT}
  687. SysDebugLn('FPC AThreads: EnterCriticalSection '+hexStr(@CS));
  688. {$ENDIF}
  689. ObtainSemaphore(PSignalSemaphore(@CS));
  690. end;
  691. function ATryEnterCriticalSection(var CS):longint;
  692. begin
  693. {$IFDEF DEBUG_MT}
  694. SysDebugLn('FPC AThreads: TryEnterCriticalSection '+hexStr(@CS));
  695. {$ENDIF}
  696. result:=DWord(AttemptSemaphore(PSignalSemaphore(@CS)));
  697. if result<>0 then
  698. result:=1;
  699. end;
  700. procedure ALeaveCriticalSection(var CS);
  701. begin
  702. {$IFDEF DEBUG_MT}
  703. SysDebugLn('FPC AThreads: LeaveCriticalSection '+hexStr(@CS));
  704. {$ENDIF}
  705. ReleaseSemaphore(PSignalSemaphore(@CS));
  706. end;
  707. procedure ADoneCriticalSection(var CS);
  708. begin
  709. {$IFDEF DEBUG_MT}
  710. SysDebugLn('FPC AThreads: DoneCriticalSection '+hexStr(@CS));
  711. {$ENDIF}
  712. { unlock as long as unlocking works to unlock it if it is recursive
  713. some Delphi code might call this function with a locked mutex }
  714. with TSignalSemaphore(CS) do
  715. while ss_NestCount > 0 do
  716. ReleaseSemaphore(PSignalSemaphore(@CS));
  717. end;
  718. // Event Stuff
  719. // Return values for WaitFor
  720. const
  721. wrSignaled = 0;
  722. wrTimeout = 1;
  723. wrAbandoned = 2;
  724. wrError = 3;
  725. // the internal AmigaEvent
  726. type
  727. TAmiEvent = record
  728. IsSet: Boolean; // the actual Event setting
  729. Manual: Boolean; // do not automatically reset the event
  730. Name: string; // Name for the event structure (needed for cross process)
  731. Waiter: Integer; // number of WaitFor waiting for this event
  732. Destroyed: Boolean; // the event is going to be destroyed, all WaitFor please leave first
  733. Sem: TSignalSemaphore; // Semaphore to protect the whole stuff
  734. end;
  735. PAmiEvent = ^TAmiEvent;
  736. // Create an Event
  737. function intBasicEventCreate(EventAttributes : Pointer;
  738. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  739. var
  740. AmiEvent: PAmiEvent;
  741. begin
  742. New(AmiEvent);
  743. AmiEvent^.isSet := InitialState;
  744. AmiEvent^.Name := Name;
  745. AmiEvent^.Waiter := 0;
  746. AmiEvent^.Manual := AManualReset;
  747. AmiEvent^.Destroyed := False;
  748. InitSemaphore(@AmiEvent^.Sem);
  749. Result := AmiEvent;
  750. end;
  751. procedure intbasiceventdestroy(state:peventstate);
  752. var
  753. AmiEvent: PAmiEvent absolute State;
  754. Waiter: Integer;
  755. begin
  756. if Assigned(AmiEvent) then
  757. begin
  758. ObtainSemaphore(@AmiEvent^.Sem);
  759. AmiEvent^.Destroyed := True; // we destroy the event
  760. ReleaseSemaphore(@AmiEvent^.Sem);
  761. repeat
  762. DosDelay(1);
  763. until AmiEvent^.Waiter <= 0;
  764. ObtainSemaphore(@AmiEvent^.Sem); // is there anyone still waiting for it?
  765. ReleaseSemaphore(@AmiEvent^.Sem);
  766. Dispose(AmiEvent);
  767. end;
  768. end;
  769. procedure intbasiceventResetEvent(state:peventstate);
  770. var
  771. AmiEvent: PAmiEvent absolute State;
  772. begin
  773. if Assigned(AmiEvent) then
  774. begin
  775. {$IFDEF DEBUG_AMIEVENT}
  776. SysDebugLn('AmiEvent: Reset Event');
  777. {$ENDIF}
  778. ObtainSemaphore(@AmiEvent^.Sem);
  779. AmiEvent^.IsSet := False;
  780. ReleaseSemaphore(@AmiEvent^.Sem);
  781. end;
  782. end;
  783. procedure intbasiceventSetEvent(state:peventstate);
  784. var
  785. AmiEvent: PAmiEvent absolute State;
  786. begin
  787. if Assigned(AmiEvent) then
  788. begin
  789. {$IFDEF DEBUG_AMIEVENT}
  790. SysDebugLn('AmiEvent: Set Event');
  791. {$ENDIF}
  792. ObtainSemaphore(@AmiEvent^.Sem);
  793. AmiEvent^.IsSet := True;
  794. ReleaseSemaphore(@AmiEvent^.Sem);
  795. end;
  796. end;
  797. // Timer stuff
  798. procedure NewList(List: PList); inline;
  799. begin
  800. with List^ do
  801. begin
  802. lh_Head := PNode(@lh_Tail);
  803. lh_Tail := nil;
  804. lh_TailPred := PNode(@lh_Head)
  805. end;
  806. end;
  807. function CreatePort(Name: PChar; Pri: LongInt): PMsgPort;
  808. var
  809. SigBit: ShortInt;
  810. Port: PMsgPort;
  811. begin
  812. Sigbit := AllocSignal(-1);
  813. if sigbit = -1 then
  814. CreatePort := nil;
  815. Port := ExecAllocMem(SizeOf(TMsgPort), MEMF_CLEAR);
  816. if Port = nil then
  817. begin
  818. FreeSignal(SigBit);
  819. CreatePort := nil;
  820. end;
  821. with port^ do
  822. begin
  823. if Assigned(Name) then
  824. mp_Node.ln_Name := Name
  825. else
  826. mp_Node.ln_Name := nil;
  827. mp_Node.ln_Pri := pri;
  828. mp_Node.ln_Type := 4;
  829. mp_Flags := 0;
  830. mp_SigBit := SigBit;
  831. mp_SigTask := FindTask(nil);
  832. end;
  833. if Assigned(Name) then
  834. AddPort(Port)
  835. else
  836. NewList(Addr(Port^.mp_MsgList));
  837. CreatePort := Port;
  838. end;
  839. procedure DeletePort(Port: PMsgPort);
  840. begin
  841. if port <> nil then
  842. begin
  843. if Port^.mp_Node.ln_Name <> nil then
  844. RemPort(Port);
  845. Port^.mp_Node.ln_Type := $FF;
  846. Port^.mp_MsgList.lh_Head := PNode(-1);
  847. FreeSignal(Port^.mp_SigBit);
  848. ExecFreeMem(Port, SizeOf(TMsgPort));
  849. end;
  850. end;
  851. function CreateExtIO(Port: PMsgPort; Size: LongInt): PIORequest;
  852. begin
  853. Result := nil;
  854. if Port <> nil then
  855. begin
  856. Result := ExecAllocMem(Size, MEMF_CLEAR);
  857. if Result <> nil then
  858. begin
  859. Result^.io_Message.mn_Node.ln_Type := 7;
  860. Result^.io_Message.mn_Length := Size;
  861. Result^.io_Message.mn_ReplyPort := Port;
  862. end;
  863. end;
  864. end;
  865. procedure DeleteExtIO (IoReq: PIORequest);
  866. begin
  867. if IoReq <> nil then
  868. begin
  869. IoReq^.io_Message.mn_Node.ln_Type := $FF;
  870. IoReq^.io_Message.mn_ReplyPort := PMsgPort(-1);
  871. IoReq^.io_Device := PDevice(-1);
  872. ExecFreeMem(IoReq, IoReq^.io_Message.mn_Length);
  873. end
  874. end;
  875. function Create_Timer(TheUnit: LongInt): PTimeRequest;
  876. var
  877. TimerPort: PMsgPort;
  878. begin
  879. Result := nil;
  880. TimerPort := CreatePort(nil, 0);
  881. if TimerPort = nil then
  882. Exit;
  883. Result := PTimeRequest(CreateExtIO(TimerPort, SizeOf(TTimeRequest)));
  884. if Result = Nil then
  885. begin
  886. DeletePort(TimerPort);
  887. Exit;
  888. end;
  889. if OpenDevice(TIMERNAME, TheUnit, PIORequest(Result), 0) <> 0 then
  890. begin
  891. DeleteExtIO(PIORequest(Result));
  892. DeletePort(TimerPort);
  893. Result := nil;
  894. end;
  895. end;
  896. Procedure Delete_Timer(WhichTimer: PTimeRequest);
  897. var
  898. WhichPort: PMsgPort;
  899. begin
  900. WhichPort := WhichTimer^.tr_Node.io_Message.mn_ReplyPort;
  901. if assigned(WhichTimer) then
  902. begin
  903. CloseDevice(PIORequest(WhichTimer));
  904. DeleteExtIO(PIORequest(WhichTimer));
  905. end;
  906. if Assigned(WhichPort) then
  907. DeletePort(WhichPort);
  908. end;
  909. function GetEventTime(TR: PTimeRequest): Int64;
  910. begin
  911. Result := -1;
  912. if tr = nil then
  913. Exit;
  914. tr^.tr_node.io_Command := TR_GETSYSTIME;
  915. DoIO(PIORequest(tr));
  916. // structure assignment
  917. Result := Int64(tr^.tr_time.TV_Secs) * 1000 + tr^.tr_time.TV_Micro div 1000;
  918. end;
  919. // End timer stuff
  920. // the mighty Waitfor routine
  921. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  922. var
  923. AmiEvent: PAmiEvent absolute State;
  924. Tr: PTimeRequest = nil;
  925. StartTime, CurTime: Int64;
  926. begin
  927. {$IFDEF DEBUG_AMIEVENT}
  928. SysDebugLn('AmiEvent: Enter WaitFor');
  929. {$ENDIF}
  930. Result := wrError;
  931. if Assigned(AmiEvent) then
  932. begin
  933. // we do an initial Check
  934. ObtainSemaphore(@AmiEvent^.Sem);
  935. if AmiEvent^.Destroyed then
  936. begin
  937. Result := wrAbandoned; // we got destroyed, so we just leave
  938. {$IFDEF DEBUG_AMIEVENT}
  939. SysDebugLn('AmiEvent: WaitFor Early Destroy');
  940. {$ENDIF}
  941. Exit;
  942. end;
  943. if AmiEvent^.IsSet then
  944. begin
  945. Result := wrSignaled; // signal Already set
  946. if not AmiEvent^.Manual then
  947. AmiEvent^.IsSet := False;
  948. {$IFDEF DEBUG_AMIEVENT}
  949. SysDebugLn('AmiEvent: WaitFor Early Signaled');
  950. {$ENDIF}
  951. Exit;
  952. end;
  953. // signal not set, so we add this call to the waiterlist
  954. Inc(AmiEvent^.Waiter);
  955. ReleaseSemaphore(@AmiEvent^.Sem);
  956. // that means we have to wait and care about the timeout -> need a timer
  957. Tr := create_timer(UNIT_MICROHZ);
  958. if not Assigned(Tr) then // cannot create timer :-O
  959. Exit;
  960. // time we started the actual waiting
  961. StartTime := GetEventTime(TR);
  962. try
  963. // the main loop, notice the breaks are inside the Obtain/Release
  964. // therefore the finally block must release it, (and no other exit allowed!)
  965. repeat
  966. CurTime := GetEventTime(TR); // to check the timeout, outside obtain/release to save some time
  967. ObtainSemaphore(@AmiEvent^.Sem);
  968. // check the status of event
  969. if AmiEvent^.Destroyed then
  970. begin
  971. Result := wrAbandoned; // we got destroyed
  972. {$IFDEF DEBUG_AMIEVENT}
  973. SysDebugLn('AmiEvent: WaitFor Destroy');
  974. {$ENDIF}
  975. break;
  976. end;
  977. if AmiEvent^.IsSet then
  978. begin
  979. Result := wrSignaled; // signal got set
  980. {$IFDEF DEBUG_AMIEVENT}
  981. SysDebugLn('AmiEvent: WaitFor Signaled');
  982. {$ENDIF}
  983. Break;
  984. end;
  985. if CurTime - StartTime > Timeout then
  986. begin
  987. Result := wrTimeOut; // we got a timeout
  988. {$IFDEF DEBUG_AMIEVENT}
  989. SysDebugLn('AmiEvent: WaitFor TimeOut');
  990. {$ENDIF}
  991. Break;
  992. end;
  993. // if we reach here, nothing happend...
  994. // we release the semaphore and wait for other threads to do something
  995. ReleaseSemaphore(@AmiEvent^.Sem);
  996. DosDelay(1);
  997. until False;
  998. finally
  999. // reset the Event if needed
  1000. if (Result = wrSignaled) and (not AmiEvent^.Manual) then
  1001. AmiEvent^.IsSet := False;
  1002. // we finished so get us away from waiter list
  1003. Dec(AmiEvent^.Waiter);
  1004. ReleaseSemaphore(@AmiEvent^.Sem); // unlock the event
  1005. Delete_timer(tr); // timer not needed anymore
  1006. end;
  1007. end;
  1008. {$IFDEF DEBUG_AMIEVENT}
  1009. SysDebugLn('AmiEvent: Leave WaitFor');
  1010. {$ENDIF}
  1011. end;
  1012. // end Event stuff
  1013. function AInitThreads : Boolean;
  1014. begin
  1015. {$ifdef DEBUG_MT}
  1016. SysDebugLn('FPC AThreads: Entering InitThreads...');
  1017. {$endif}
  1018. result:=true;
  1019. // We assume that if you set the thread manager, the application is multithreading.
  1020. InitAThreading;
  1021. ThreadID := TThreadID(GetCurrentThreadInfo);
  1022. end;
  1023. function ADoneThreads : Boolean;
  1024. begin
  1025. result:=true;
  1026. end;
  1027. procedure SetAThreadManager;
  1028. begin
  1029. with AThreadManager do begin
  1030. InitManager :=@AInitThreads;
  1031. DoneManager :=@ADoneThreads;
  1032. BeginThread :=@ABeginThread;
  1033. EndThread :=@AEndThread;
  1034. SuspendThread :=@ASuspendThread;
  1035. ResumeThread :=@AResumeThread;
  1036. KillThread :=@AKillThread;
  1037. ThreadSwitch :=@AThreadSwitch;
  1038. CloseThread :=@ACloseThread;
  1039. WaitForThreadTerminate :=@AWaitForThreadTerminate;
  1040. ThreadSetPriority :=@AThreadSetPriority;
  1041. ThreadGetPriority :=@AThreadGetPriority;
  1042. GetCurrentThreadId :=@AGetCurrentThreadId;
  1043. InitCriticalSection :=@AInitCriticalSection;
  1044. DoneCriticalSection :=@ADoneCriticalSection;
  1045. EnterCriticalSection :=@AEnterCriticalSection;
  1046. TryEnterCriticalSection:=@ATryEnterCriticalSection;
  1047. LeaveCriticalSection :=@ALeaveCriticalSection;
  1048. InitThreadVar :=@AInitThreadVar;
  1049. RelocateThreadVar :=@ARelocateThreadVar;
  1050. AllocateThreadVars :=@AAllocateThreadVars;
  1051. ReleaseThreadVars :=@AReleaseThreadVars;
  1052. BasicEventCreate :=@intBasicEventCreate;
  1053. BasicEventDestroy :=@intBasicEventDestroy;
  1054. BasicEventResetEvent :=@intBasicEventResetEvent;
  1055. BasicEventSetEvent :=@intBasicEventSetEvent;
  1056. BasiceventWaitFor :=@intBasicEventWaitFor;
  1057. rtlEventCreate :=@intrtlEventCreate;
  1058. rtlEventDestroy :=@intrtlEventDestroy;
  1059. rtlEventSetEvent :=@intrtlEventSetEvent;
  1060. rtlEventResetEvent :=@intrtlEventResetEvent;
  1061. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  1062. rtleventWaitFor :=@intrtleventWaitFor;
  1063. end;
  1064. SetThreadManager(AThreadManager);
  1065. end;
  1066. Procedure InitSystemThreads; external name '_FPC_InitSystemThreads';
  1067. { This should only be called from the finalization }
  1068. procedure WaitForAllThreads;
  1069. var
  1070. p: PThreadInfo;
  1071. pn: PThreadInfo;
  1072. begin
  1073. { If we are the main thread exiting, we have to wait for our subprocesses to
  1074. exit. Because AmigaOS won't clean up for us. Also, after exiting the main
  1075. thread the OS unloads all the code segments with code potentially still
  1076. running in the background... So even waiting here forever is better than
  1077. exiting with active threads, which will most likely just kill the OS
  1078. immediately. (KB) }
  1079. ObtainSemaphore(@AThreadListSemaphore);
  1080. {$IFDEF DEBUG_MT}
  1081. if AThreadListLen > 1 then
  1082. begin
  1083. SysDebugLn('FPC AThreads: We have registered subthreads, checking their status...');
  1084. if CountRunningThreads(AThreadList) > 1 then
  1085. SysDebugLn('FPC AThreads: We have running subthreads, waiting for them to exit...');
  1086. end;
  1087. {$ENDIF}
  1088. while CountRunningThreads(AThreadList) > 1 do
  1089. begin
  1090. ReleaseSemaphore(@AThreadListSemaphore);
  1091. DOSDelay(1);
  1092. { Reobtain the semaphore... }
  1093. ObtainSemaphore(@AThreadListSemaphore);
  1094. end;
  1095. if AThreadListLen > 1 then
  1096. begin
  1097. {$IFDEF DEBUG_MT}
  1098. SysDebugLn('FPC AThreads: All threads exited but some lacking cleanup - trying to free up resources...');
  1099. {$ENDIF}
  1100. p:=AThreadList;
  1101. while p <> nil do
  1102. begin
  1103. pn:=p^.nextThread;
  1104. if not p^.mainThread then
  1105. RemoveFromThreadList(AThreadList,p^.threadID);
  1106. p:=pn;
  1107. end;
  1108. end
  1109. else
  1110. begin
  1111. {$IFDEF DEBUG_MT}
  1112. SysDebugLn('FPC AThreads: All threads exited normally.');
  1113. {$ENDIF}
  1114. end;
  1115. ReleaseSemaphore(@AThreadListSemaphore);
  1116. end;
  1117. initialization
  1118. initsystemthreads;
  1119. {$IFDEF DEBUG_MT}
  1120. SysDebugLn('FPC AThreads: Unit Initialization');
  1121. {$ENDIF}
  1122. if ThreadingAlreadyUsed then
  1123. begin
  1124. writeln('Threading has been used before athreads was initialized.');
  1125. writeln('Make athreads one of the first units in your uses clause!');
  1126. runerror(211);
  1127. end;
  1128. AThreadList:=nil;
  1129. AThreadListLen:=0;
  1130. AThreadNum:=-1; { Mainthread will be 0. }
  1131. InitSemaphore(@AThreadListSemaphore);
  1132. SetAThreadManager;
  1133. {$IFDEF DEBUG_MT}
  1134. SysDebugLn('FPC AThreads: Unit Initialization Done');
  1135. {$ENDIF}
  1136. finalization
  1137. {$IFDEF DEBUG_MT}
  1138. SysDebugLn('FPC AThreads: Unit Finalization');
  1139. {$ENDIF}
  1140. WaitForAllThreads;
  1141. end.