athreads.pp 26 KB

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