wasmthreads.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2022 by Michael Van Canneyt,
  4. member of the Free Pascal development team.
  5. wasm threading support implementation
  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. {$modeswitch advancedrecords}
  14. {$DEFINE DEBUG_MT}
  15. unit wasmthreads;
  16. interface
  17. Procedure SetWasmThreadManager;
  18. implementation
  19. Uses
  20. WebAssembly, wasiapi;
  21. {*****************************************************************************
  22. System unit import
  23. *****************************************************************************}
  24. procedure fpc_threaderror; [external name 'FPC_THREADERROR'];
  25. Type
  26. TTimeLockResult = (tlrOK,tlrTimeout,tlrError);
  27. TFPWasmMutex = record
  28. _lock : Longint;
  29. _owner : Pointer;
  30. function TryLock : Boolean;
  31. function Lock : Boolean;
  32. function TimedLock(aTimeOut : Longint) : TTimeLockResult;
  33. function Unlock : Boolean;
  34. end;
  35. TFPWasmEvent = record
  36. _mutex : TFPWasmMutex;
  37. _isset : Boolean;
  38. end;
  39. PFPWasmThread = ^TFPWasmThread;
  40. TFPWasmThread = record
  41. ThreadID : Integer;
  42. Next : PFPWasmThread;
  43. Previous : PFPWasmThread;
  44. end;
  45. Var
  46. MainThread : TFPWasmThread;
  47. threadvarblocksize : dword = 0;
  48. TLSInitialized : Integer = 0;
  49. {$IFDEF DEBUG_MT}
  50. Type
  51. TSmallString = string[100];
  52. Procedure SetTLSMemory(aValue : Pointer);
  53. begin
  54. fpc_wasm32_init_tls(aValue);
  55. end;
  56. Function GetTLSMemory : Pointer;
  57. begin
  58. Result:=fpc_wasm32_tls_base;
  59. end;
  60. Procedure RawWrite(var S : TSmallString);
  61. begin
  62. // ToDo
  63. end;
  64. {$ENDIF DEBUG_MT}
  65. procedure WasmInitThreadvar(var offset : dword;size : dword);
  66. begin
  67. threadvarblocksize:=align(threadvarblocksize, fpc_wasm32_tls_align);
  68. offset:=threadvarblocksize;
  69. inc(threadvarblocksize,size);
  70. end;
  71. procedure WasmAllocateThreadVars;
  72. var
  73. tlsMemBlock : pointer;
  74. tlsBlockSize : Integer;
  75. begin
  76. tlsBlockSize:=fpc_wasm32_tls_size;
  77. if threadvarblocksize<>tlsBlocksize then
  78. Writeln('Warning : block sizes differ: ',tlsBlocksize,'<>',threadvarblocksize,'(calculated) !');
  79. // DataIndex:=Pointer(Fpmmap(nil,threadvarblocksize,3,MAP_PRIVATE+MAP_ANONYMOUS,-1,0));
  80. FillChar(DataIndex^,threadvarblocksize,0);
  81. // pthread_setspecific(tlskey,dataindex);
  82. end;
  83. procedure WasmThreadCleanup(p: pointer); cdecl;
  84. {$ifdef DEBUG_MT}
  85. var
  86. s: TSmallString; // not an ansistring
  87. {$endif DEBUG_MT}
  88. begin
  89. {$ifdef DEBUG_MT}
  90. s := 'finishing externally started thread'#10;
  91. RawWrite(s);
  92. {$endif DEBUG_MT}
  93. { Restore tlskey value as it may already have been set to null,
  94. in which case
  95. a) DoneThread can't release the memory
  96. b) accesses to threadvars from DoneThread or anything it
  97. calls would allocate new threadvar memory
  98. }
  99. { clean up }
  100. DoneThread;
  101. pthread_setspecific(CleanupKey,nil);
  102. end;
  103. procedure HookThread;
  104. { Set up externally created thread }
  105. begin
  106. WasmAllocateThreadVars;
  107. InitThread(1000000000);
  108. pthread_setspecific(CleanupKey,getTlsMemory);
  109. end;
  110. function WasmRelocateThreadvar(offset : dword) : pointer;
  111. var
  112. P : Pointer;
  113. begin
  114. P:=GetTLSMemory;
  115. if (P=Nil) then
  116. begin
  117. HookThread;
  118. P:=GetTLSMemory;
  119. end;
  120. WasmRelocateThreadvar:=P+Offset;
  121. end;
  122. procedure WasmReleaseThreadVars;
  123. begin
  124. Fpmunmap(pointer(pthread_getspecific(tlskey)),threadvarblocksize);
  125. end;
  126. function WasmThreadMain(param : pointer) : pointer;
  127. var
  128. {$ifdef DEBUG_MT}
  129. s: TSmallString; // not an ansistring
  130. {$endif DEBUG_MT}
  131. begin
  132. {$ifdef DEBUG_MT}
  133. s := 'New thread started, initing threadvars'#10;
  134. RawWrite(s);
  135. {$endif DEBUG_MT}
  136. { Must be first, many system unit things depend on threadvars}
  137. WasmAllocateThreadVars;
  138. { Copy parameter to local data }
  139. {$ifdef DEBUG_MT}
  140. s := 'New thread started, initialising ...'#10;
  141. RawWrite(s);
  142. {$endif DEBUG_MT}
  143. ti:=pthreadinfo(param)^;
  144. { Initialize thread }
  145. InitThread(ti.stklen);
  146. dispose(pthreadinfo(param));
  147. { Start thread function }
  148. {$ifdef DEBUG_MT}
  149. writeln('Jumping to thread function');
  150. {$endif DEBUG_MT}
  151. WasmThreadMain:=pointer(ti.f(ti.p));
  152. DoneThread;
  153. pthread_exit(WasmThreadMain);
  154. end;
  155. Procedure InitWasmTLS;
  156. begin
  157. if (InterLockedExchange(longint(TLSInitialized),1) = 0) then
  158. begin
  159. { We're still running in single thread mode, setup the TLS }
  160. pthread_key_create(@TLSKey,nil);
  161. InitThreadVars(@WasmRelocateThreadvar);
  162. { used to clean up threads that we did not create ourselves:
  163. a) the default value for a key (and hence also this one) in
  164. new threads is NULL, and if it's still like that when the
  165. thread terminates, nothing will happen
  166. b) if it's non-NULL, the destructor routine will be called
  167. when the thread terminates
  168. -> we will set it to 1 if the threadvar relocation routine is
  169. called from a thread we did not create, so that we can
  170. clean up everything at the end }
  171. pthread_key_create(@CleanupKey,@WasmthreadCleanup);
  172. end
  173. end;
  174. function WasmBeginThread(sa : Pointer;stacksize : PtrUInt;
  175. ThreadFunction : tthreadfunc;p : pointer;
  176. creationFlags : dword; var ThreadId : TThreadId) : TThreadID;
  177. var
  178. ti : pthreadinfo;
  179. thread_attr : pthread_attr_t;
  180. {$ifdef DEBUG_MT}
  181. S : TSmallString;
  182. {$endif DEBUG_MT}
  183. begin
  184. {$ifdef DEBUG_MT}
  185. S:='Creating new thread';
  186. RawWrite(S);
  187. {$endif DEBUG_MT}
  188. { Initialize multithreading if not done }
  189. if not TLSInitialized then
  190. InitWasmTLS;
  191. if not IsMultiThread then
  192. begin
  193. { We're still running in single thread mode, lazy initialize thread support }
  194. LazyInitThreading;
  195. IsMultiThread:=true;
  196. end;
  197. { the only way to pass data to the newly created thread
  198. in a MT safe way, is to use the heap }
  199. new(ti);
  200. ti^.f:=ThreadFunction;
  201. ti^.p:=p;
  202. ti^.stklen:=stacksize;
  203. { call pthread_create }
  204. {$ifdef DEBUG_MT}
  205. S:='Starting new thread';
  206. RawWrite(S);
  207. {$endif DEBUG_MT}
  208. pthread_attr_init(@thread_attr);
  209. {$if not defined(HAIKU)and not defined(BEOS) and not defined(ANDROID)}
  210. {$if defined (solaris) or defined (netbsd) }
  211. pthread_attr_setinheritsched(@thread_attr, PTHREAD_INHERIT_SCHED);
  212. {$else not solaris}
  213. pthread_attr_setinheritsched(@thread_attr, PTHREAD_EXPLICIT_SCHED);
  214. {$endif not solaris}
  215. {$ifend}
  216. // will fail under linux -- apparently unimplemented
  217. pthread_attr_setscope(@thread_attr, PTHREAD_SCOPE_PROCESS);
  218. // don't create detached, we need to be able to join (waitfor) on
  219. // the newly created thread!
  220. //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
  221. // set the stack size
  222. if (pthread_attr_setstacksize(@thread_attr, stacksize)<>0) or
  223. // and create the thread
  224. (pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0) then
  225. begin
  226. dispose(ti);
  227. threadid := TThreadID(0);
  228. end;
  229. CBeginThread:=threadid;
  230. pthread_attr_destroy(@thread_attr);
  231. {$ifdef DEBUG_MT}
  232. Str(ptrint(CBeginThread),S);
  233. S:= 'BeginThread returning '+S;
  234. RawWrite(S);
  235. {$endif DEBUG_MT}
  236. end;
  237. procedure WasmEndThread(ExitCode : DWord);
  238. begin
  239. DoneThread;
  240. pthread_detach(pthread_t(pthread_self()));
  241. pthread_exit(pointer(ptrint(ExitCode)));
  242. end;
  243. function WasmSuspendThread (threadHandle : TThreadID) : dword;
  244. // Not supported
  245. begin
  246. result:=dword(-1);
  247. end;
  248. function WasmResumeThread (threadHandle : TThreadID) : dword;
  249. // Not supported
  250. begin
  251. result:=dword(-1);
  252. end;
  253. procedure WasmThreadSwitch; {give time to other threads}
  254. begin
  255. // Not supported
  256. end;
  257. function WasmKillThread (threadHandle : TThreadID) : dword;
  258. begin
  259. pthread_detach(pthread_t(threadHandle));
  260. WasmKillThread := pthread_cancel(pthread_t(threadHandle));
  261. end;
  262. function WasmCloseThread (threadHandle : TThreadID) : dword;
  263. begin
  264. result:=0;
  265. end;
  266. function WasmWaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword; {0=no timeout}
  267. var
  268. LResultP: Pointer;
  269. begin
  270. pthread_join(pthread_t(threadHandle), @LResultP);
  271. WasmWaitForThreadTerminate := dword(LResultP);
  272. end;
  273. function WasmThreadSetPriority (threadHandle : TThreadID; Prio: longint): boolean; {-15..+15, 0=normal}
  274. begin
  275. result:=false;
  276. end;
  277. function WasmThreadGetPriority (threadHandle : TThreadID): Integer;
  278. begin
  279. result:=0;
  280. end;
  281. function CGetCurrentThreadId : TThreadID;
  282. begin
  283. CGetCurrentThreadId := TThreadID (pthread_self());
  284. end;
  285. procedure CSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  286. {$if defined(Linux) or defined(Android)}
  287. var
  288. CuttedName: AnsiString;
  289. {$endif}
  290. begin
  291. {$if defined(Linux) or defined(Android)}
  292. if ThreadName = '' then
  293. Exit;
  294. {$ifdef dynpthreads}
  295. if Assigned(pthread_setname_np) then
  296. {$endif dynpthreads}
  297. begin
  298. // length restricted to 16 characters including terminating null byte
  299. CuttedName:=Copy(ThreadName, 1, 15);
  300. if threadHandle=TThreadID(-1) then
  301. begin
  302. pthread_setname_np(pthread_self(), @CuttedName[1]);
  303. end
  304. else
  305. begin
  306. pthread_setname_np(pthread_t(threadHandle), @CuttedName[1]);
  307. end;
  308. end;
  309. {$elseif defined(Darwin) or defined(iphonesim)}
  310. {$ifdef dynpthreads}
  311. if Assigned(pthread_setname_np) then
  312. {$endif dynpthreads}
  313. begin
  314. // only allowed to set from within the thread
  315. if threadHandle=TThreadID(-1) then
  316. pthread_setname_np(@ThreadName[1]);
  317. end;
  318. {$else}
  319. {$Warning SetThreadDebugName needs to be implemented}
  320. {$endif}
  321. end;
  322. procedure CSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  323. begin
  324. {$if defined(Linux) or defined(Android)}
  325. {$ifdef dynpthreads}
  326. if Assigned(pthread_setname_np) then
  327. {$endif dynpthreads}
  328. begin
  329. CSetThreadDebugNameA(threadHandle, AnsiString(ThreadName));
  330. end;
  331. {$elseif defined(Darwin) or defined(iphonesim)}
  332. {$ifdef dynpthreads}
  333. if Assigned(pthread_setname_np) then
  334. {$endif dynpthreads}
  335. begin
  336. CSetThreadDebugNameA(threadHandle, AnsiString(ThreadName));
  337. end;
  338. {$else}
  339. {$Warning SetThreadDebugName needs to be implemented}
  340. {$endif}
  341. end;
  342. {*****************************************************************************
  343. Delphi/Win32 compatibility
  344. *****************************************************************************}
  345. procedure CInitCriticalSection(var CS);
  346. var
  347. MAttr : pthread_mutexattr_t;
  348. res: longint;
  349. begin
  350. res:=pthread_mutexattr_init(@MAttr);
  351. if res=0 then
  352. begin
  353. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  354. if res=0 then
  355. res := pthread_mutex_init(@CS,@MAttr)
  356. else
  357. { No recursive mutex support :/ }
  358. fpc_threaderror
  359. end
  360. else
  361. res:= pthread_mutex_init(@CS,NIL);
  362. pthread_mutexattr_destroy(@MAttr);
  363. if res <> 0 then
  364. fpc_threaderror;
  365. end;
  366. procedure CEnterCriticalSection(var CS);
  367. begin
  368. if pthread_mutex_lock(@CS) <> 0 then
  369. fpc_threaderror
  370. end;
  371. function CTryEnterCriticalSection(var CS):longint;
  372. begin
  373. if pthread_mutex_Trylock(@CS)=0 then
  374. result:=1 // succes
  375. else
  376. result:=0; // failure
  377. end;
  378. procedure CLeaveCriticalSection(var CS);
  379. begin
  380. if pthread_mutex_unlock(@CS) <> 0 then
  381. fpc_threaderror
  382. end;
  383. procedure CDoneCriticalSection(var CS);
  384. begin
  385. { unlock as long as unlocking works to unlock it if it is recursive
  386. some Delphi code might call this function with a locked mutex }
  387. while pthread_mutex_unlock(@CS)=0 do
  388. ;
  389. if pthread_mutex_destroy(@CS) <> 0 then
  390. fpc_threaderror;
  391. end;
  392. {*****************************************************************************
  393. Semaphore routines
  394. *****************************************************************************}
  395. type
  396. TPthreadCondition = pthread_cond_t;
  397. TPthreadMutex = pthread_mutex_t;
  398. Tbasiceventstate=record
  399. FCondVar: TPthreadCondition;
  400. {$if defined(Linux) and not defined(Android)}
  401. FAttr: pthread_condattr_t;
  402. FClockID: longint;
  403. {$ifend}
  404. FEventSection: TPthreadMutex;
  405. FWaiters: longint;
  406. FIsSet,
  407. FManualReset,
  408. FDestroying : Boolean;
  409. end;
  410. plocaleventstate = ^tbasiceventstate;
  411. // peventstate=pointer;
  412. Const
  413. wrSignaled = 0;
  414. wrTimeout = 1;
  415. wrAbandoned= 2;
  416. wrError = 3;
  417. function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialState : Boolean;const Name : ansistring):pEventState;
  418. var
  419. MAttr : pthread_mutexattr_t;
  420. res : cint;
  421. {$if defined(Linux) and not defined(Android)}
  422. timespec: ttimespec;
  423. {$ifend}
  424. begin
  425. new(plocaleventstate(result));
  426. plocaleventstate(result)^.FManualReset:=AManualReset;
  427. plocaleventstate(result)^.FWaiters:=0;
  428. plocaleventstate(result)^.FDestroying:=False;
  429. plocaleventstate(result)^.FIsSet:=InitialState;
  430. {$if defined(Linux) and not defined(Android)}
  431. res := pthread_condattr_init(@plocaleventstate(result)^.FAttr);
  432. if (res <> 0) then
  433. begin
  434. FreeMem(result);
  435. fpc_threaderror;
  436. end;
  437. if clock_gettime(CLOCK_MONOTONIC_RAW, @timespec) = 0 then
  438. begin
  439. res := pthread_condattr_setclock(@plocaleventstate(result)^.FAttr, CLOCK_MONOTONIC_RAW);
  440. end
  441. else
  442. begin
  443. res := -1; // No support for CLOCK_MONOTONIC_RAW
  444. end;
  445. if (res = 0) then
  446. begin
  447. plocaleventstate(result)^.FClockID := CLOCK_MONOTONIC_RAW;
  448. end
  449. else
  450. begin
  451. res := pthread_condattr_setclock(@plocaleventstate(result)^.FAttr, CLOCK_MONOTONIC);
  452. if (res = 0) then
  453. begin
  454. plocaleventstate(result)^.FClockID := CLOCK_MONOTONIC;
  455. end
  456. else
  457. begin
  458. pthread_condattr_destroy(@plocaleventstate(result)^.FAttr);
  459. FreeMem(result);
  460. fpc_threaderror;
  461. end;
  462. end;
  463. res := pthread_cond_init(@plocaleventstate(result)^.FCondVar, @plocaleventstate(result)^.FAttr);
  464. if (res <> 0) then
  465. begin
  466. pthread_condattr_destroy(@plocaleventstate(result)^.FAttr);
  467. FreeMem(result);
  468. fpc_threaderror;
  469. end;
  470. {$else}
  471. res := pthread_cond_init(@plocaleventstate(result)^.FCondVar, nil);
  472. if (res <> 0) then
  473. begin
  474. FreeMem(result);
  475. fpc_threaderror;
  476. end;
  477. {$ifend}
  478. res:=pthread_mutexattr_init(@MAttr);
  479. if res=0 then
  480. begin
  481. res:=pthread_mutexattr_settype(@MAttr,longint(_PTHREAD_MUTEX_RECURSIVE));
  482. if Res=0 then
  483. Res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,@MAttr)
  484. else
  485. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  486. end
  487. else
  488. res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
  489. pthread_mutexattr_destroy(@MAttr);
  490. if res <> 0 then
  491. begin
  492. pthread_cond_destroy(@plocaleventstate(result)^.FCondVar);
  493. {$if defined(Linux) and not defined(Android)}
  494. pthread_condattr_destroy(@plocaleventstate(result)^.FAttr);
  495. {$ifend}
  496. FreeMem(result);
  497. fpc_threaderror;
  498. end;
  499. end;
  500. procedure Intbasiceventdestroy(state:peventstate);
  501. begin
  502. { safely mark that we are destroying this event }
  503. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  504. plocaleventstate(state)^.FDestroying:=true;
  505. { send a signal to all threads that are waiting }
  506. pthread_cond_broadcast(@plocaleventstate(state)^.FCondVar);
  507. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  508. { now wait until they've finished their business }
  509. while (plocaleventstate(state)^.FWaiters <> 0) do
  510. cThreadSwitch;
  511. { and clean up }
  512. pthread_cond_destroy(@plocaleventstate(state)^.Fcondvar);
  513. {$if defined(Linux) and not defined(Android)}
  514. pthread_condattr_destroy(@plocaleventstate(state)^.FAttr);
  515. {$ifend}
  516. pthread_mutex_destroy(@plocaleventstate(state)^.FEventSection);
  517. dispose(plocaleventstate(state));
  518. end;
  519. procedure IntbasiceventResetEvent(state:peventstate);
  520. begin
  521. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  522. plocaleventstate(state)^.fisset:=false;
  523. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  524. end;
  525. procedure IntbasiceventSetEvent(state:peventstate);
  526. begin
  527. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  528. plocaleventstate(state)^.Fisset:=true;
  529. if not(plocaleventstate(state)^.FManualReset) then
  530. pthread_cond_signal(@plocaleventstate(state)^.Fcondvar)
  531. else
  532. pthread_cond_broadcast(@plocaleventstate(state)^.Fcondvar);
  533. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  534. end;
  535. function IntbasiceventWaitFor(Timeout : Cardinal;state:peventstate) : longint;
  536. var
  537. timespec: ttimespec;
  538. errres: cint;
  539. isset: boolean;
  540. tnow : timeval;
  541. begin
  542. { safely check whether we are being destroyed, if so immediately return. }
  543. { otherwise (under the same mutex) increase the number of waiters }
  544. pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
  545. if (plocaleventstate(state)^.FDestroying) then
  546. begin
  547. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  548. result := wrAbandoned;
  549. exit;
  550. end;
  551. { not a regular inc() because it may happen simulatneously with the }
  552. { interlockeddecrement() at the end }
  553. interlockedincrement(plocaleventstate(state)^.FWaiters);
  554. //Wait without timeout using pthread_cond_wait
  555. if Timeout = $FFFFFFFF then
  556. begin
  557. while (not plocaleventstate(state)^.FIsSet) and (not plocaleventstate(state)^.FDestroying) do
  558. pthread_cond_wait(@plocaleventstate(state)^.Fcondvar, @plocaleventstate(state)^.feventsection);
  559. end
  560. else
  561. begin
  562. //Wait with timeout using pthread_cond_timedwait
  563. {$if defined(Linux) and not defined(Android)}
  564. if clock_gettime(plocaleventstate(state)^.FClockID, @timespec) <> 0 then
  565. begin
  566. Result := Ord(wrError);
  567. Exit;
  568. end;
  569. timespec.tv_sec := timespec.tv_sec + (clong(timeout) div 1000);
  570. timespec.tv_nsec := ((clong(timeout) mod 1000) * 1000000) + (timespec.tv_nsec);
  571. {$else}
  572. // TODO: FIX-ME: Also use monotonic clock for other *nix targets
  573. fpgettimeofday(@tnow, nil);
  574. timespec.tv_sec := tnow.tv_sec + (clong(timeout) div 1000);
  575. timespec.tv_nsec := ((clong(timeout) mod 1000) * 1000000) + (tnow.tv_usec * 1000);
  576. {$ifend}
  577. if timespec.tv_nsec >= 1000000000 then
  578. begin
  579. inc(timespec.tv_sec);
  580. dec(timespec.tv_nsec, 1000000000);
  581. end;
  582. errres := 0;
  583. while (not plocaleventstate(state)^.FDestroying) and
  584. (not plocaleventstate(state)^.FIsSet) and
  585. (errres<>ESysETIMEDOUT) do
  586. errres := pthread_cond_timedwait(@plocaleventstate(state)^.Fcondvar,
  587. @plocaleventstate(state)^.feventsection,
  588. @timespec);
  589. end;
  590. isset := plocaleventstate(state)^.FIsSet;
  591. { if ManualReset=false, reset the event immediately. }
  592. if (plocaleventstate(state)^.FManualReset=false) then
  593. plocaleventstate(state)^.FIsSet := false;
  594. //check the results...
  595. if plocaleventstate(state)^.FDestroying then
  596. Result := wrAbandoned
  597. else
  598. if IsSet then
  599. Result := wrSignaled
  600. else
  601. begin
  602. if errres=ESysETIMEDOUT then
  603. Result := wrTimeout
  604. else
  605. Result := wrError;
  606. end;
  607. pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
  608. { don't put this above the previous pthread_mutex_unlock, because }
  609. { otherwise we can get errors in case an object is destroyed between }
  610. { end of the wait/sleep loop and the signalling above. }
  611. { The pthread_mutex_unlock above takes care of the memory barrier }
  612. interlockeddecrement(plocaleventstate(state)^.FWaiters);
  613. end;
  614. function intRTLEventCreate: PRTLEvent;
  615. var p:pintrtlevent;
  616. begin
  617. new(p);
  618. if not assigned(p) then
  619. fpc_threaderror;
  620. if pthread_cond_init(@p^.condvar, nil)<>0 then
  621. begin
  622. dispose(p);
  623. fpc_threaderror;
  624. end;
  625. if pthread_mutex_init(@p^.mutex, nil)<>0 then
  626. begin
  627. pthread_cond_destroy(@p^.condvar);
  628. dispose(p);
  629. fpc_threaderror;
  630. end;
  631. p^.isset:=false;
  632. result:=PRTLEVENT(p);
  633. end;
  634. procedure intRTLEventDestroy(AEvent: PRTLEvent);
  635. var p:pintrtlevent;
  636. begin
  637. p:=pintrtlevent(aevent);
  638. pthread_cond_destroy(@p^.condvar);
  639. pthread_mutex_destroy(@p^.mutex);
  640. dispose(p);
  641. end;
  642. procedure intRTLEventSetEvent(AEvent: PRTLEvent);
  643. var p:pintrtlevent;
  644. begin
  645. p:=pintrtlevent(aevent);
  646. pthread_mutex_lock(@p^.mutex);
  647. p^.isset:=true;
  648. pthread_cond_signal(@p^.condvar);
  649. pthread_mutex_unlock(@p^.mutex);
  650. end;
  651. procedure intRTLEventResetEvent(AEvent: PRTLEvent);
  652. var p:pintrtlevent;
  653. begin
  654. p:=pintrtlevent(aevent);
  655. pthread_mutex_lock(@p^.mutex);
  656. p^.isset:=false;
  657. pthread_mutex_unlock(@p^.mutex);
  658. end;
  659. procedure intRTLEventWaitFor(AEvent: PRTLEvent);
  660. var p:pintrtlevent;
  661. begin
  662. p:=pintrtlevent(aevent);
  663. pthread_mutex_lock(@p^.mutex);
  664. while not p^.isset do pthread_cond_wait(@p^.condvar, @p^.mutex);
  665. p^.isset:=false;
  666. pthread_mutex_unlock(@p^.mutex);
  667. end;
  668. procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
  669. var
  670. p : pintrtlevent;
  671. errres : cint;
  672. timespec : ttimespec;
  673. tnow : timeval;
  674. begin
  675. p:=pintrtlevent(aevent);
  676. fpgettimeofday(@tnow,nil);
  677. timespec.tv_sec:=tnow.tv_sec+(timeout div 1000);
  678. timespec.tv_nsec:=(timeout mod 1000)*1000000 + tnow.tv_usec*1000;
  679. if timespec.tv_nsec >= 1000000000 then
  680. begin
  681. inc(timespec.tv_sec);
  682. dec(timespec.tv_nsec, 1000000000);
  683. end;
  684. errres:=0;
  685. pthread_mutex_lock(@p^.mutex);
  686. while (not p^.isset) and
  687. (errres <> ESysETIMEDOUT) do
  688. begin
  689. errres:=pthread_cond_timedwait(@p^.condvar, @p^.mutex, @timespec);
  690. end;
  691. p^.isset:=false;
  692. pthread_mutex_unlock(@p^.mutex);
  693. end;
  694. type
  695. threadmethod = procedure of object;
  696. Function CInitThreads : Boolean;
  697. begin
  698. {$ifdef DEBUG_MT}
  699. Writeln('Entering InitThreads.');
  700. {$endif}
  701. {$ifndef dynpthreads}
  702. Result:=True;
  703. {$else}
  704. Result:=LoadPthreads;
  705. {$endif}
  706. ThreadID := TThreadID (pthread_self());
  707. {$ifdef DEBUG_MT}
  708. Writeln('InitThreads : ',Result);
  709. {$endif DEBUG_MT}
  710. // We assume that if you set the thread manager, the application is multithreading.
  711. InitCTLS;
  712. end;
  713. Function CDoneThreads : Boolean;
  714. begin
  715. {$ifndef dynpthreads}
  716. Result:=True;
  717. {$else}
  718. Result:=UnloadPthreads;
  719. {$endif}
  720. end;
  721. Var
  722. CThreadManager : TThreadManager;
  723. Procedure SetCThreadManager;
  724. begin
  725. With CThreadManager do begin
  726. InitManager :=@WasmInitThreads;
  727. DoneManager :=@WasmDoneThreads;
  728. BeginThread :=@WasmBeginThread;
  729. EndThread :=@WasmEndThread;
  730. SuspendThread :=@WasmSuspendThread;
  731. ResumeThread :=@WasmResumeThread;
  732. KillThread :=@WasmKillThread;
  733. ThreadSwitch :=@WasmThreadSwitch;
  734. CloseThread :=@WasmCloseThread;
  735. WaitForThreadTerminate :=@WasmWaitForThreadTerminate;
  736. ThreadSetPriority :=@WasmThreadSetPriority;
  737. ThreadGetPriority :=@WasmThreadGetPriority;
  738. GetCurrentThreadId :=@WasmGetCurrentThreadId;
  739. SetThreadDebugNameA :=@WasmSetThreadDebugNameA;
  740. SetThreadDebugNameU :=@WasmSetThreadDebugNameU;
  741. InitCriticalSection :=@WasmInitCriticalSection;
  742. DoneCriticalSection :=@WasmDoneCriticalSection;
  743. EnterCriticalSection :=@WasmEnterCriticalSection;
  744. TryEnterCriticalSection:=@WasmTryEnterCriticalSection;
  745. LeaveCriticalSection :=@WasmLeaveCriticalSection;
  746. InitThreadVar :=@WasmInitThreadVar;
  747. RelocateThreadVar :=@WasmRelocateThreadVar;
  748. AllocateThreadVars :=@WasmAllocateThreadVars;
  749. ReleaseThreadVars :=@WasmReleaseThreadVars;
  750. BasicEventCreate :=@intBasicEventCreate;
  751. BasicEventDestroy :=@intBasicEventDestroy;
  752. BasicEventResetEvent :=@intBasicEventResetEvent;
  753. BasicEventSetEvent :=@intBasicEventSetEvent;
  754. BasiceventWaitFor :=@intBasiceventWaitFor;
  755. rtlEventCreate :=@intrtlEventCreate;
  756. rtlEventDestroy :=@intrtlEventDestroy;
  757. rtlEventSetEvent :=@intrtlEventSetEvent;
  758. rtlEventResetEvent :=@intrtlEventResetEvent;
  759. rtleventWaitForTimeout :=@intrtleventWaitForTimeout;
  760. rtleventWaitFor :=@intrtleventWaitFor;
  761. end;
  762. SetThreadManager(CThreadManager);
  763. end;
  764. initialization
  765. if ThreadingAlreadyUsed then
  766. begin
  767. writeln('Threading has been used before cthreads was initialized.');
  768. writeln('Make wasmthreads one of the first units in your uses clause.');
  769. runerror(211);
  770. end;
  771. SetWasmThreadManager;
  772. finalization
  773. end.