athreads.pp 27 KB

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