athreads.pp 37 KB

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