athreads.pp 36 KB

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