athreads.pp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  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. implementation
  17. { enable this to compile athreads easily outside the RTL }
  18. {.$DEFINE ATHREADS_STANDALONE}
  19. {$IFDEF ATHREADS_STANDALONE}
  20. uses
  21. exec, amigados, utility;
  22. {$ELSE}
  23. { * Include required system specific includes * }
  24. {$include execd.inc}
  25. {$include execf.inc}
  26. {$include timerd.inc}
  27. {$include doslibd.inc}
  28. {$include doslibf.inc}
  29. {$ENDIF}
  30. const
  31. threadvarblocksize : dword = 0;
  32. var
  33. SubThreadBaseName: String = 'FPC Subthread';
  34. {.$define DEBUG_MT}
  35. type
  36. TThreadOperation = ( toNone, toStart, toResume, toExit );
  37. type
  38. PThreadMsg = ^TThreadMsg;
  39. PThreadInfo = ^TThreadInfo;
  40. TThreadInfo = record
  41. threadVars: Pointer; { have threadvars ptr as first field, so no offset is needed to access it (faster) }
  42. threadVarsSize: DWord; { size of the allocated threadvars block }
  43. nextThread: PThreadInfo; { threadinfos are a linked list, using this field }
  44. threadPtr: PProcess; { our thread pointer, as returned by CreateNewProc(). invalid after exited field is true! }
  45. threadID: TThreadID; { thread Unique ID }
  46. stackLen: PtrUInt; { stack size the thread was construced with }
  47. exitCode: Pointer; { exitcode after the process has exited }
  48. f: TThreadFunc; { ThreadFunc function pointer }
  49. p: Pointer; { ThreadFunc argument }
  50. flags: dword; { Flags this thread were created with }
  51. num: longint; { This was the "num"th thread to created }
  52. mainthread: boolean; { true if this is our main thread }
  53. exited: boolean; { true if the thread has exited, and can be cleaned up }
  54. startSuspended: boolean; { true if the thread was started suspended, and not resumed yet }
  55. suspended: boolean; { true if the thread is currently suspended }
  56. mutex: TSignalSemaphore; { thread's mutex. locked during the thread's life. }
  57. name: String; { Thread's name }
  58. end;
  59. TThreadMsg = record
  60. tm_MsgNode : TMessage;
  61. tm_ThreadInfo: PThreadInfo;
  62. tm_Operation : TThreadOperation;
  63. end;
  64. var
  65. AThreadManager: TThreadManager;
  66. AThreadList: PThreadInfo;
  67. AThreadListLen: LongInt;
  68. AThreadNum: LongInt;
  69. AThreadListSemaphore: TSignalSemaphore;
  70. { Simple IntToStr() replacement which works with ShortStrings }
  71. function IToStr(const i: LongInt): String;
  72. begin
  73. Str(I,result);
  74. end;
  75. {$IFDEF DEBUG_MT}
  76. function IToHStr(const i: LongInt): String;
  77. begin
  78. result:=HexStr(Pointer(i));
  79. end;
  80. {$ENDIF}
  81. { Function to add a thread to the running threads list }
  82. procedure AddToThreadList(var l: PThreadInfo; ti: PThreadInfo);
  83. var
  84. p : PThreadInfo;
  85. inList: Boolean;
  86. begin
  87. inList:=False;
  88. ObtainSemaphore(@AThreadListSemaphore);
  89. if l = nil then
  90. { if the list is not yet allocated, the newly added
  91. threadinfo will be the first item }
  92. l:=ti
  93. else
  94. begin
  95. { otherwise, look for the last item and append }
  96. p:=l;
  97. while (p^.nextThread<>nil) do p:=p^.nextThread;
  98. p^.nextThread:=ti;
  99. end;
  100. inc(AThreadNum);
  101. ti^.num:=AThreadNum;
  102. inc(AThreadListLen);
  103. {$IFDEF DEBUG_MT}
  104. SysDebugLn('FPC AThreads: thread ID:'+IToHStr(ti^.threadID)+' added, now '+IToStr(AThreadListLen)+' thread(s) in list.');
  105. {$ENDIF}
  106. ReleaseSemaphore(@AThreadListSemaphore);
  107. end;
  108. { Function to remove a thread from running threads list }
  109. function RemoveFromThreadList(var l: PThreadInfo; threadID: TThreadID): boolean;
  110. var
  111. p : PThreadInfo;
  112. pprev : PThreadInfo;
  113. inList : Boolean;
  114. tmpNext: PThreadInfo;
  115. tmpInfo: PThreadInfo;
  116. begin
  117. inList:=False;
  118. if l=nil then
  119. begin
  120. RemoveFromThreadList:=inList;
  121. exit;
  122. end;
  123. ObtainSemaphore(@AThreadListSemaphore);
  124. p:=l;
  125. pprev:=nil;
  126. while (p <> nil) and (p^.threadID <> threadID) do
  127. begin
  128. pprev:=p;
  129. p:=p^.nextThread;
  130. end;
  131. if p <> nil then
  132. begin
  133. tmpNext:=p^.nextThread;
  134. if not p^.mainthread and p^.exited then
  135. begin
  136. {$IFDEF DEBUG_MT}
  137. SysDebugLn('FPC AThreads: Releasing resources for thread ID:'+IToHStr(threadID));
  138. if (p^.threadVars <> nil) or (p^.threadVarsSize <> 0) then
  139. SysDebugLn('FPC AThreads: WARNING, threadvars area wasn''t properly freed!'+IToHStr(threadID));
  140. {$ENDIF}
  141. dispose(p);
  142. if pprev <> nil then
  143. pprev^.nextThread:=tmpNext;
  144. Dec(AThreadListLen);
  145. end
  146. else
  147. begin
  148. {$IFDEF DEBUG_MT}
  149. SysDebugLn('FPC AThreads: Error! Attempt to remove threadID, which is the mainthread or not exited:'+IToHStr(threadID));
  150. {$ENDIF}
  151. inList:=false;
  152. end;
  153. end
  154. {$IFDEF DEBUG_MT}
  155. else
  156. SysDebugLn('FPC AThreads: Error! Attempt to remove threadID, which is not in list:'+IToHstr(threadID))
  157. {$ENDIF}
  158. ;
  159. ReleaseSemaphore(@AThreadListSemaphore);
  160. RemoveFromThreadList:=inList;
  161. end;
  162. { Function to return a function's ThreadInfo based on the threadID }
  163. function GetThreadInfo(var l: PThreadInfo; threadID: TThreadID): PThreadInfo;
  164. var
  165. p : PThreadInfo;
  166. inList: Boolean;
  167. begin
  168. inList:=False;
  169. GetThreadInfo:=nil;
  170. if l = nil then
  171. exit;
  172. ObtainSemaphoreShared(@AThreadListSemaphore);
  173. p:=l;
  174. while (p <> nil) and (p^.threadID <> threadID) do
  175. p:=p^.nextThread;
  176. GetThreadInfo:=p;
  177. ReleaseSemaphore(@AThreadListSemaphore);
  178. end;
  179. { Get current thread's ThreadInfo structure }
  180. function GetCurrentThreadInfo: PThreadInfo;
  181. begin
  182. result:=PThreadInfo(PProcess(FindTask(nil))^.pr_Task.tc_UserData);
  183. end;
  184. { Returns the number of threads still not exited in our threadlist }
  185. function CountRunningThreads(var l: PThreadInfo): LongInt;
  186. var
  187. p: PThreadInfo;
  188. begin
  189. CountRunningThreads:=0;
  190. ObtainSemaphoreShared(@AThreadListSemaphore);
  191. p:=l;
  192. while p <> nil do
  193. begin
  194. inc(CountRunningThreads,ord(not p^.exited));
  195. p:=p^.nextThread;
  196. end;
  197. ReleaseSemaphore(@AThreadListSemaphore);
  198. end;
  199. { Helper function for IPC }
  200. procedure SendMessageToThread(var threadMsg: TThreadMsg; p: PThreadInfo; const op: TThreadOperation; waitReply: boolean);
  201. var
  202. replyPort: PMsgPort;
  203. begin
  204. replyPort:=@PProcess(FindTask(nil))^.pr_MsgPort;
  205. FillChar(threadMsg,sizeof(threadMsg),0);
  206. with threadMsg do
  207. begin
  208. with tm_MsgNode do
  209. begin
  210. mn_Node.ln_Type:=NT_MESSAGE;
  211. mn_Length:=SizeOf(TThreadMsg);
  212. if waitReply then
  213. mn_ReplyPort:=replyPort
  214. else
  215. mn_ReplyPort:=nil;
  216. end;
  217. tm_ThreadInfo:=p;
  218. tm_Operation:=op;
  219. end;
  220. PutMsg(@p^.threadPtr^.pr_MsgPort,@threadMsg);
  221. if waitReply then
  222. begin
  223. WaitPort(replyPort);
  224. GetMsg(replyPort);
  225. end;
  226. end;
  227. procedure SetAThreadBaseName(s: String);
  228. begin
  229. ObtainSemaphore(@AThreadListSemaphore);
  230. SubThreadBaseName:=s;
  231. ReleaseSemaphore(@AThreadListSemaphore);
  232. end;
  233. function GetAThreadBaseName: String;
  234. begin
  235. ObtainSemaphoreShared(@AThreadListSemaphore);
  236. GetAThreadBaseName:=SubThreadBaseName;
  237. ReleaseSemaphore(@AThreadListSemaphore);
  238. end;
  239. procedure AInitThreadvar(var offset : dword;size : dword);
  240. begin
  241. {$IFDEF DEBUG_MT}
  242. {SysDebugLn('FPC AThreads: InitThreadvar');}
  243. {$ENDIF}
  244. offset:=threadvarblocksize;
  245. inc(threadvarblocksize,size);
  246. end;
  247. function ARelocateThreadvar(offset : dword) : pointer;
  248. var
  249. p: PThreadInfo;
  250. begin
  251. {$IFDEF DEBUG_MT}
  252. {SysDebugLn('FPC AThreads: RelocateThreadvar');}
  253. {$ENDIF}
  254. p:=GetCurrentThreadInfo;
  255. if (p <> nil) and (p^.threadVars <> nil) then
  256. result:=p^.threadVars + Offset
  257. else
  258. result:=nil;
  259. end;
  260. procedure AAllocateThreadVars;
  261. var
  262. p: PThreadInfo;
  263. begin
  264. { we've to allocate the memory from system }
  265. { because the FPC heap management uses }
  266. { exceptions which use threadvars but }
  267. { these aren't allocated yet ... }
  268. { allocate room on the heap for the thread vars }
  269. p:=GetCurrentThreadInfo;
  270. if p <> nil then
  271. begin
  272. {$ifdef DEBUG_MT}
  273. SysDebugLn('FPC AThreads: Allocating threadvars, ID:'+IToHStr(p^.threadID));
  274. {$endif}
  275. {$ifdef AMIGA}
  276. ObtainSemaphore(ASYS_heapSemaphore);
  277. {$endif}
  278. p^.threadVars:=AllocPooled(ASYS_heapPool,threadvarblocksize);
  279. if p^.threadVars = nil then
  280. SysDebugLn('FPC AThreads: Failed to allocate threadvar memory!')
  281. else
  282. begin
  283. p^.threadVarsSize:=threadvarblocksize;
  284. FillChar(p^.threadVars^,threadvarblocksize,0);
  285. end;
  286. {$ifdef AMIGA}
  287. ReleaseSemaphore(ASYS_heapSemaphore);
  288. {$endif}
  289. end
  290. else
  291. begin
  292. {$ifdef DEBUG_MT}
  293. SysDebugLn('FPC AThreads: AllocateThreadVars: tc_UserData of this process was nil!')
  294. {$endif}
  295. end;
  296. end;
  297. procedure AReleaseThreadVars;
  298. var
  299. p: PThreadInfo;
  300. begin
  301. p:=GetCurrentThreadInfo;
  302. if (p <> nil) and (p^.threadVars <> nil) then
  303. begin
  304. {$ifdef DEBUG_MT}
  305. SysDebugLn('FPC AThreads: Releasing threadvars, ID:'+IToHStr(p^.threadID));
  306. {$endif}
  307. {$ifdef AMIGA}
  308. ObtainSemaphore(ASYS_heapSemaphore);
  309. {$endif}
  310. FreePooled(ASYS_heapPool,p^.threadVars,p^.threadVarsSize);
  311. p^.threadVars:=nil;
  312. p^.threadVarsSize:=0;
  313. {$ifdef AMIGA}
  314. ReleaseSemaphore(ASYS_heapSemaphore);
  315. {$endif}
  316. end
  317. else
  318. begin
  319. {$ifdef DEBUG_MT}
  320. SysDebugLn('FPC AThreads: ReleaseThreadVars: tc_UserData or threadVars area of this process was nil!')
  321. {$endif}
  322. end;
  323. end;
  324. procedure InitAThreading;
  325. var
  326. threadInfo: PThreadInfo;
  327. p: PProcess;
  328. begin
  329. if (InterLockedExchange(longint(IsMultiThread),ord(true)) = 0) then
  330. begin
  331. { We're still running in single thread mode, setup the TLS }
  332. {$ifdef DEBUG_MT}
  333. SysDebugLn('FPC AThreads: Entering multithreaded mode...');
  334. {$endif}
  335. p:=PProcess(FindTask(nil));
  336. new(threadInfo);
  337. FillChar(threadInfo^,sizeof(TThreadInfo),0);
  338. p^.pr_Task.tc_UserData:=threadInfo;
  339. threadInfo^.mainThread:=true;
  340. InitSemaphore(@threadInfo^.mutex);
  341. ObtainSemaphore(@threadInfo^.mutex);
  342. threadInfo^.threadPtr:=p;
  343. threadInfo^.threadID:=TThreadID(threadInfo);
  344. InitThreadVars(@ARelocateThreadvar);
  345. AddToThreadList(AThreadList,threadInfo);
  346. end;
  347. end;
  348. procedure ThreadFunc; cdecl;
  349. var
  350. thisThread: PProcess;
  351. threadMsg: PThreadMsg;
  352. resumeMsg: PThreadMsg;
  353. exitSuspend: boolean; // true if we have to exit instead of resuming
  354. threadInfo: PThreadInfo;
  355. begin
  356. thisThread:=PProcess(FindTask(nil));
  357. { wait for our start message to arrive, then fetch it }
  358. WaitPort(@thisThread^.pr_MsgPort);
  359. threadMsg:=PThreadMsg(GetMsg(@thisThread^.pr_MsgPort));
  360. { fetch existing threadinfo from the start message, and set
  361. it to tc_userData, so we can proceed with threadvars }
  362. threadInfo:=threadMsg^.tm_ThreadInfo;
  363. thisThread^.pr_Task.tc_userData:=threadInfo;
  364. {$ifdef DEBUG_MT}
  365. SysDebugLn('FPC AThreads: Entering subthread function, ID:'+hexStr(threadInfo));
  366. {$endif}
  367. { Obtain the threads' mutex, used for exit sync }
  368. ObtainSemaphore(@threadInfo^.mutex);
  369. { Allocate local thread vars, this must be the first thing,
  370. because the exception management and io depends on threadvars }
  371. AAllocateThreadVars;
  372. { Rename the thread into something sensible }
  373. if threadInfo^.name <> '' then
  374. begin
  375. {$ifdef DEBUG_MT}
  376. { this line can't be before threadvar allocation }
  377. SysDebugLn('FPC AThreads: Renaming thread ID:'+hexStr(threadInfo)+' to '+threadInfo^.name);
  378. {$endif}
  379. thisThread^.pr_Task.tc_Node.ln_Name:=PChar(@threadInfo^.name[1]);
  380. end;
  381. { Reply the message, so the calling thread could continue }
  382. { note that threadMsg was allocated on the caller's task, so }
  383. { it will be invalid below this point }
  384. ReplyMsg(PMessage(threadMsg));
  385. { if creating a suspended thread, wait for the wakeup message to arrive }
  386. { then check if we actually have to resume, or exit }
  387. exitSuspend:=false;
  388. if threadInfo^.startSuspended then
  389. begin
  390. {$ifdef DEBUG_MT}
  391. SysDebugLn('FPC AThreads: Suspending subthread on entry, ID:'+hexStr(threadInfo));
  392. {$endif}
  393. WaitPort(@thisThread^.pr_MsgPort);
  394. resumeMsg:=PThreadMsg(GetMsg(@thisThread^.pr_MsgPort));
  395. exitSuspend:=resumeMsg^.tm_Operation <> toResume;
  396. threadInfo^.startSuspended:=false;
  397. ReplyMsg(PMessage(resumeMsg));
  398. {$ifdef DEBUG_MT}
  399. SysDebugLn('FPC AThreads: Resuming subthread on entry, ID:'+hexStr(threadInfo)+', resumed only to exit: '+IToStr(ord(exitSuspend)));
  400. {$endif}
  401. end;
  402. { Finally, call the user code }
  403. if not exitSuspend then
  404. begin
  405. InitThread(threadInfo^.stackLen);
  406. DoThreadInitProcChain;
  407. threadInfo^.exitCode:=Pointer(threadInfo^.f(threadInfo^.p));
  408. DoThreadExitProcChain;
  409. DoneThread;
  410. end;
  411. {$ifdef DEBUG_MT}
  412. SysDebugLn('FPC AThreads: Exiting Subthread function, ID:'+hexStr(threadInfo));
  413. {$endif}
  414. Forbid();
  415. threadInfo^.exited:=true;
  416. { Finally, Release our exit mutex. }
  417. ReleaseSemaphore(@threadInfo^.mutex);
  418. end;
  419. function CreateNewProcess(const Tags : Array Of PtrUInt) : PProcess;
  420. begin
  421. result:=CreateNewProc(@Tags[0]);
  422. end;
  423. function ABeginThread(sa : Pointer;stacksize : PtrUInt;
  424. ThreadFunction : tthreadfunc;p : pointer;
  425. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  426. var
  427. threadInfo: PThreadInfo;
  428. threadMsg: TThreadMsg;
  429. threadName: String;
  430. subThread: PProcess;
  431. begin
  432. ABeginThread:=TThreadID(0);
  433. {$ifdef DEBUG_MT}
  434. SysDebugLn('FPC AThreads: Creating new thread...');
  435. {$endif DEBUG_MT}
  436. { Initialize multithreading if not done }
  437. if not IsMultiThread then
  438. InitAThreading;
  439. { the only way to pass data to the newly created thread
  440. in a MT safe way, is to use the heap }
  441. new(threadInfo);
  442. FillChar(threadInfo^,sizeof(TThreadInfo),0);
  443. InitSemaphore(@threadInfo^.mutex);
  444. threadInfo^.f:=ThreadFunction;
  445. threadInfo^.p:=p;
  446. if (creationFlags and STACK_SIZE_PARAM_IS_A_RESERVATION) > 0 then
  447. threadInfo^.stackLen:=stacksize
  448. else
  449. threadInfo^.stackLen:=System.StackLength; { inherit parent's stack size }
  450. threadInfo^.startSuspended:=(creationFlags and CREATE_SUSPENDED) > 0;
  451. {$ifdef DEBUG_MT}
  452. SysDebugLn('FPC AThreads: Starting new thread... Stack size: '+IToStr(threadInfo^.stackLen));
  453. {$endif}
  454. subThread:=CreateNewProcess([NP_Entry,PtrUInt(@ThreadFunc),
  455. {$IFDEF MORPHOS}
  456. NP_CodeType,CODETYPE_PPC,
  457. NP_PPCStackSize,threadInfo^.stacklen,
  458. {$ELSE}
  459. NP_StackSize,threadInfo^.stacklen,
  460. {$ENDIF}
  461. TAG_DONE]);
  462. if subThread = nil then
  463. begin
  464. {$ifdef DEBUG_MT}
  465. SysDebugLn('FPC AThreads: Failed to start the subthread!');
  466. {$endif}
  467. exit;
  468. end;
  469. ThreadID:=TThreadID(threadInfo);
  470. threadInfo^.threadPtr:=subThread;
  471. threadInfo^.threadID:=ThreadID;
  472. AddToThreadList(AThreadList,threadInfo);
  473. { the thread should be started, and waiting for our start message, so send it }
  474. {$ifdef DEBUG_MT}
  475. SysDebugLn('FPC AThreads: Sending start message to subthread and waiting for reply, ID:'+IToHStr(threadID));
  476. {$endif}
  477. { AddToThreadList assigned us a number, so use it to name the thread }
  478. threadInfo^.name:=GetAThreadBaseName+' #'+IToStr(threadInfo^.num);
  479. SendMessageToThread(threadMsg,threadInfo,toStart,true);
  480. ABeginThread:=ThreadId;
  481. {$ifdef DEBUG_MT}
  482. SysDebugLn('FPC AThreads: Thread created successfully, ID:'+IToHStr(threadID));
  483. {$endif}
  484. end;
  485. procedure AEndThread(ExitCode : DWord);
  486. begin
  487. { Do not call DoneThread here. It will be called by the threadfunction, when it exits. }
  488. end;
  489. function ASuspendThread (threadHandle : TThreadID) : dword;
  490. var
  491. p: PThreadInfo;
  492. m: PThreadMsg;
  493. begin
  494. ASuspendThread:=0;
  495. if GetCurrentThreadID = threadHandle then
  496. begin
  497. p:=GetThreadInfo(AThreadList,threadHandle);
  498. if p <> nil then
  499. begin
  500. p^.suspended:=true;
  501. while p^.suspended do
  502. begin
  503. WaitPort(@p^.threadPtr^.pr_MsgPort);
  504. m:=PThreadMsg(GetMsg(@p^.threadPtr^.pr_MsgPort));
  505. if m^.tm_Operation = toResume then
  506. p^.suspended:=false
  507. else
  508. {$ifdef DEBUG_MT}
  509. SysDebugLn('FPC AThreads: Got message during suspend, but it wasn''t toResume! ID:'+IToHStr(threadHandle))
  510. {$endif}
  511. ;
  512. ReplyMsg(PMessage(m));
  513. end;
  514. end;
  515. end
  516. else
  517. begin
  518. {$ifdef DEBUG_MT}
  519. SysDebugLn('FPC AThreads: SuspendThread called for ID:'+IToHStr(threadHandle)+' which is not the current thread!');
  520. {$endif}
  521. result:=dword(-1);
  522. end;
  523. end;
  524. function AResumeThread (threadHandle : TThreadID) : dword;
  525. var
  526. m: TThreadMsg;
  527. p: PThreadInfo;
  528. begin
  529. AResumeThread:=0;
  530. Forbid();
  531. p:=GetThreadInfo(AThreadList,threadHandle);
  532. if (p <> nil) and (p^.suspended or p^.startSuspended) then
  533. begin
  534. {$ifdef DEBUG_MT}
  535. SysDebugLn('FPC AThreads: Waiting for thread to resume, ID:'+IToHStr(threadHandle));
  536. {$endif}
  537. { WaitPort in SendMessageToThread will break the Forbid() state... }
  538. SendMessageToThread(m,p,toResume,true);
  539. AResumeThread:=0;
  540. end
  541. else
  542. begin
  543. {$ifdef DEBUG_MT}
  544. SysDebugLn('FPC AThreads: Error, attempt to resume a non-suspended thread, or invalid thread ID:'+IToHStr(threadHandle));
  545. {$endif}
  546. AResumeThread:=dword(-1);
  547. end;
  548. Permit();
  549. end;
  550. procedure AThreadSwitch; {give time to other threads}
  551. begin
  552. { On Unix, this calls sched_yield();
  553. Harry 'Piru' Sintonen recommended to emulate this on Amiga systems with
  554. exec/Forbid-exec/Permit pair which is pretty fast to execute and will
  555. trigger a rescheduling.
  556. Another idea by Frank Mariak was to use exec/SetTaskPri() with the same
  557. priority }
  558. Forbid();
  559. Permit();
  560. end;
  561. function AKillThread (threadHandle : TThreadID) : dword;
  562. begin
  563. {$ifdef DEBUG_MT}
  564. SysDebugLn('FPC AThreads: unsupported operation: KillThread called for ID:'+IToHStr(threadHandle));
  565. {$endif}
  566. // cannot be properly supported on Amiga
  567. AKillThread:=dword(-1);
  568. end;
  569. function ACloseThread (threadHandle : TThreadID) : dword;
  570. begin
  571. {$WARNING The return value here seems to be undocumented}
  572. RemoveFromThreadList(AThreadList, threadHandle);
  573. result:=0;
  574. end;
  575. function AWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  576. var
  577. p: PThreadInfo;
  578. m: TThreadMsg;
  579. begin
  580. {.$WARNING Support for timeout argument is not implemented}
  581. { But since CThreads uses pthread_join, which has also no timeout,
  582. I don't think this is a big issue. (KB) }
  583. AWaitForThreadTerminate:=0;
  584. Forbid();
  585. p:=GetThreadInfo(AThreadList,threadHandle);
  586. if (p <> nil) then
  587. begin
  588. if not p^.exited then
  589. begin
  590. {$ifdef DEBUG_MT}
  591. SysDebugLn('FPC AThreads: Waiting for thread to exit, ID:'+IToHStr(threadHandle));
  592. {$endif}
  593. { WaitPort in SendMessageToThread will break the Forbid() state... }
  594. if p^.startSuspended then
  595. begin
  596. SendMessageToThread(m,p,toExit,true);
  597. {$ifdef DEBUG_MT}
  598. SysDebugLn('FPC AThreads: Signaled start-suspended thread to exit, ID:'+IToHStr(threadHandle));
  599. {$endif}
  600. end;
  601. { Wait for the thread to exit... }
  602. Permit();
  603. ObtainSemaphore(@p^.mutex);
  604. ReleaseSemaphore(@p^.mutex);
  605. Forbid();
  606. end
  607. else
  608. {$ifdef DEBUG_MT}
  609. SysDebugLn('FPC AThreads: Thread already exited, ID:'+IToHStr(threadHandle));
  610. {$endif}
  611. AWaitForThreadTerminate:=DWord(p^.exitCode);
  612. end
  613. else
  614. begin
  615. {$ifdef DEBUG_MT}
  616. SysDebugLn('FPC AThreads: Error, attempt to wait for invalid thread ID to exit, ID:'+IToHStr(threadHandle));
  617. {$endif}
  618. AWaitForThreadTerminate:=dword(-1); { Return non-zero code on error. }
  619. end;
  620. Permit();
  621. end;
  622. function AThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  623. begin
  624. {$Warning ThreadSetPriority needs to be implemented}
  625. result:=false;
  626. end;
  627. function AThreadGetPriority (threadHandle : TThreadID): Integer;
  628. begin
  629. {$Warning ThreadGetPriority needs to be implemented}
  630. result:=0;
  631. end;
  632. function AGetCurrentThreadId : TThreadID;
  633. begin
  634. AGetCurrentThreadId := TThreadID(GetCurrentThreadInfo);
  635. end;
  636. Type PINTRTLEvent = ^TINTRTLEvent;
  637. TINTRTLEvent = record
  638. isset: boolean;
  639. end;
  640. Function intRTLEventCreate: PRTLEvent;
  641. var p:pintrtlevent;
  642. begin
  643. new(p);
  644. result:=PRTLEVENT(p);
  645. end;
  646. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  647. var p:pintrtlevent;
  648. begin
  649. p:=pintrtlevent(aevent);
  650. dispose(p);
  651. end;
  652. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  653. var p:pintrtlevent;
  654. begin
  655. p:=pintrtlevent(aevent);
  656. p^.isset:=true;
  657. end;
  658. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  659. var p:pintrtlevent;
  660. begin
  661. p:=pintrtlevent(aevent);
  662. p^.isset:=false;
  663. end;
  664. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  665. var p:pintrtlevent;
  666. begin
  667. p:=pintrtlevent(aevent);
  668. p^.isset:=false;
  669. end;
  670. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  671. var
  672. p : pintrtlevent;
  673. begin
  674. p:=pintrtlevent(aevent);
  675. end;
  676. procedure AInitCriticalSection(var CS);
  677. begin
  678. {$IFDEF DEBUG_MT}
  679. SysDebugLn('FPC AThreads: InitCriticalSection '+hexStr(@CS));
  680. {$ENDIF}
  681. InitSemaphore(PSignalSemaphore(@CS));
  682. end;
  683. procedure AEnterCriticalSection(var CS);
  684. begin
  685. {$IFDEF DEBUG_MT}
  686. SysDebugLn('FPC AThreads: EnterCriticalSection '+hexStr(@CS));
  687. {$ENDIF}
  688. ObtainSemaphore(PSignalSemaphore(@CS));
  689. end;
  690. function ATryEnterCriticalSection(var CS):longint;
  691. begin
  692. {$IFDEF DEBUG_MT}
  693. SysDebugLn('FPC AThreads: TryEnterCriticalSection '+hexStr(@CS));
  694. {$ENDIF}
  695. result:=DWord(AttemptSemaphore(PSignalSemaphore(@CS)));
  696. if result<>0 then
  697. result:=1;
  698. end;
  699. procedure ALeaveCriticalSection(var CS);
  700. begin
  701. {$IFDEF DEBUG_MT}
  702. SysDebugLn('FPC AThreads: LeaveCriticalSection '+hexStr(@CS));
  703. {$ENDIF}
  704. ReleaseSemaphore(PSignalSemaphore(@CS));
  705. end;
  706. procedure ADoneCriticalSection(var CS);
  707. begin
  708. {$IFDEF DEBUG_MT}
  709. SysDebugLn('FPC AThreads: DoneCriticalSection '+hexStr(@CS));
  710. {$ENDIF}
  711. { unlock as long as unlocking works to unlock it if it is recursive
  712. some Delphi code might call this function with a locked mutex }
  713. with TSignalSemaphore(CS) do
  714. while ss_NestCount > 0 do
  715. ReleaseSemaphore(PSignalSemaphore(@CS));
  716. end;
  717. function intBasicEventCreate(EventAttributes : Pointer;
  718. AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  719. begin
  720. end;
  721. procedure intbasiceventdestroy(state:peventstate);
  722. begin
  723. end;
  724. procedure intbasiceventResetEvent(state:peventstate);
  725. begin
  726. end;
  727. procedure intbasiceventSetEvent(state:peventstate);
  728. begin
  729. end;
  730. function intbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  731. begin
  732. end;
  733. function ASemaphoreInit: Pointer;
  734. begin
  735. result:=nil;
  736. end;
  737. procedure ASemaphoreDestroy(const FSem: Pointer);
  738. begin
  739. end;
  740. procedure ASemaphoreWait(const FSem: Pointer);
  741. begin
  742. end;
  743. procedure ASemaphorePost(const FSem: Pointer);
  744. begin
  745. end;
  746. function AInitThreads : Boolean;
  747. begin
  748. {$ifdef DEBUG_MT}
  749. SysDebugLn('FPC AThreads: Entering InitThreads...');
  750. {$endif}
  751. result:=true;
  752. // We assume that if you set the thread manager, the application is multithreading.
  753. InitAThreading;
  754. ThreadID := TThreadID(GetCurrentThreadInfo);
  755. end;
  756. function ADoneThreads : Boolean;
  757. begin
  758. result:=true;
  759. end;
  760. procedure SetAThreadManager;
  761. begin
  762. with AThreadManager do begin
  763. InitManager :=@AInitThreads;
  764. DoneManager :=@ADoneThreads;
  765. BeginThread :=@ABeginThread;
  766. EndThread :=@AEndThread;
  767. SuspendThread :=@ASuspendThread;
  768. ResumeThread :=@AResumeThread;
  769. KillThread :=@AKillThread;
  770. ThreadSwitch :=@AThreadSwitch;
  771. CloseThread :=@ACloseThread;
  772. WaitForThreadTerminate :=@AWaitForThreadTerminate;
  773. ThreadSetPriority :=@AThreadSetPriority;
  774. ThreadGetPriority :=@AThreadGetPriority;
  775. GetCurrentThreadId :=@AGetCurrentThreadId;
  776. InitCriticalSection :=@AInitCriticalSection;
  777. DoneCriticalSection :=@ADoneCriticalSection;
  778. EnterCriticalSection :=@AEnterCriticalSection;
  779. TryEnterCriticalSection:=@ATryEnterCriticalSection;
  780. LeaveCriticalSection :=@ALeaveCriticalSection;
  781. InitThreadVar :=@AInitThreadVar;
  782. RelocateThreadVar :=@ARelocateThreadVar;
  783. AllocateThreadVars :=@AAllocateThreadVars;
  784. ReleaseThreadVars :=@AReleaseThreadVars;
  785. BasicEventCreate :=@intBasicEventCreate;
  786. BasicEventDestroy :=@intBasicEventDestroy;
  787. BasicEventResetEvent :=@intBasicEventResetEvent;
  788. BasicEventSetEvent :=@intBasicEventSetEvent;
  789. BasiceventWaitFor :=@intBasicEventWaitFor;
  790. rtlEventCreate :=@intrtlEventCreate;
  791. rtlEventDestroy :=@intrtlEventDestroy;
  792. rtlEventSetEvent :=@intrtlEventSetEvent;
  793. rtlEventResetEvent :=@intrtlEventResetEvent;
  794. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  795. rtleventWaitFor :=@intrtleventWaitFor;
  796. // semaphores
  797. SemaphoreInit :=@ASemaphoreInit;
  798. SemaphoreDestroy :=@ASemaphoreDestroy;
  799. SemaphoreWait :=@ASemaphoreWait;
  800. SemaphorePost :=@ASemaphorePost;
  801. end;
  802. SetThreadManager(AThreadManager);
  803. end;
  804. Procedure InitSystemThreads; external name '_FPC_InitSystemThreads';
  805. { This should only be called from the finalization }
  806. procedure WaitForAllThreads;
  807. var
  808. p: PThreadInfo;
  809. pn: PThreadInfo;
  810. begin
  811. { If we are the main thread exiting, we have to wait for our subprocesses to
  812. exit. Because AmigaOS won't clean up for us. Also, after exiting the main
  813. thread the OS unloads all the code segments with code potentially still
  814. running in the background... So even waiting here forever is better than
  815. exiting with active threads, which will most likely just kill the OS
  816. immediately. (KB) }
  817. ObtainSemaphore(@AThreadListSemaphore);
  818. {$IFDEF DEBUG_MT}
  819. if AThreadListLen > 1 then
  820. begin
  821. SysDebugLn('FPC AThreads: We have registered subthreads, checking their status...');
  822. if CountRunningThreads(AThreadList) > 1 then
  823. SysDebugLn('FPC AThreads: We have running subthreads, waiting for them to exit...');
  824. end;
  825. {$ENDIF}
  826. while CountRunningThreads(AThreadList) > 1 do
  827. begin
  828. ReleaseSemaphore(@AThreadListSemaphore);
  829. DOSDelay(1);
  830. { Reobtain the semaphore... }
  831. ObtainSemaphore(@AThreadListSemaphore);
  832. end;
  833. if AThreadListLen > 1 then
  834. begin
  835. {$IFDEF DEBUG_MT}
  836. SysDebugLn('FPC AThreads: All threads exited but some lacking cleanup - trying to free up resources...');
  837. {$ENDIF}
  838. p:=AThreadList;
  839. while p <> nil do
  840. begin
  841. pn:=p^.nextThread;
  842. if not p^.mainThread then
  843. RemoveFromThreadList(AThreadList,p^.threadID);
  844. p:=pn;
  845. end;
  846. end
  847. else
  848. begin
  849. {$IFDEF DEBUG_MT}
  850. SysDebugLn('FPC AThreads: All threads exited normally.');
  851. {$ENDIF}
  852. end;
  853. ReleaseSemaphore(@AThreadListSemaphore);
  854. end;
  855. initialization
  856. initsystemthreads;
  857. {$IFDEF DEBUG_MT}
  858. SysDebugLn('FPC AThreads: Unit Initialization');
  859. {$ENDIF}
  860. if ThreadingAlreadyUsed then
  861. begin
  862. writeln('Threading has been used before athreads was initialized.');
  863. writeln('Make athreads one of the first units in your uses clause!');
  864. runerror(211);
  865. end;
  866. AThreadList:=nil;
  867. AThreadListLen:=0;
  868. AThreadNum:=-1; { Mainthread will be 0. }
  869. InitSemaphore(@AThreadListSemaphore);
  870. SetAThreadManager;
  871. {$IFDEF DEBUG_MT}
  872. SysDebugLn('FPC AThreads: Unit Initialization Done');
  873. {$ENDIF}
  874. finalization
  875. {$IFDEF DEBUG_MT}
  876. SysDebugLn('FPC AThreads: Unit Finalization');
  877. {$ENDIF}
  878. WaitForAllThreads;
  879. end.