athreads.pp 36 KB

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