athreads.pp 38 KB

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