athreads.pp 37 KB

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