systhrd.inc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. {%MainUnit system.pp}
  2. {
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2022 by Nikolay Nikolov,
  5. member of the Free Pascal development team.
  6. WASI threading support implementation
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$ifndef FPC_WASM_THREADS}
  14. {$fatal This file shouldn't be included if thread support is disabled!}
  15. {$endif FPC_WASM_THREADS}
  16. {$DEFINE FPC_WASM_MAIN_THREAD_CAN_WAIT}
  17. {$UNDEF FPC_WASM_WORKER_THREADS_CAN_WAIT}
  18. {//$DEFINE DEBUGWASMTHREADS}
  19. Const
  20. MaxThreadSignal = 1000; // maximum threads to signal
  21. Type
  22. TThreadState = (tsNone,tsInit,tsRunning,tsCanceling,tsExit);
  23. TOSTime = __wasi_timestamp_t;
  24. // Forwards used in mutex
  25. Function GetClockTime: TOSTime; forward;
  26. Function IsWaitAllowed : Boolean; forward;
  27. Function GetSelfThread : TThreadID; forward;
  28. Function GetThreadState(aThread : TThreadID) : TThreadState; forward;
  29. {$i wasmmem.inc}
  30. {$i wasmmutex.inc}
  31. Type
  32. PWasmRTLEvent = ^TWasmRTLEvent;
  33. TWasmRTLEvent = record
  34. Signal : Longint;
  35. Destroying : Boolean;
  36. end;
  37. PWasmThread = ^TWasmThread;
  38. TWasmThread = Record
  39. InitialStackPointer : Pointer;
  40. InitTLSBase : Pointer;
  41. ID : LongInt; // Allocated by host
  42. ThreadFunction : TThreadFunc;
  43. ThreadFunctionArg : Pointer;
  44. State : TThreadState;
  45. DoneEvent : PWasmRTLEvent;
  46. Running : TWasmMutex;
  47. ExitCode : Cardinal;
  48. StackBlock : Pointer;
  49. TLSBlock : Pointer;
  50. StackSize : PtrUInt;
  51. ThreadName : Array of byte; // UTF8 name
  52. end;
  53. { EWasmThreadTerminate }
  54. EWasmThreadTerminate = class(TObject)
  55. strict private
  56. FExitCode : DWord;
  57. public
  58. constructor Create(AExitCode: DWord);
  59. property ExitCode: DWord read FExitCode;
  60. end;
  61. Var
  62. MainThread : TWasmThread;
  63. WasiThreadManager : TThreadManager;
  64. GlobalIsWorkerThread : Longint; section 'WebAssembly.Global';
  65. GlobalIsMainThread : Longint; section 'WebAssembly.Global';
  66. GlobalIsThreadBlockable : Longint; section 'WebAssembly.Global';
  67. GlobalCurrentThread : PWasmThread; section 'WebAssembly.Global';
  68. { EWasmThreadTerminate }
  69. constructor EWasmThreadTerminate.Create(AExitCode: DWord);
  70. begin
  71. FExitCode:=AExitCode;
  72. end;
  73. // Forward functions
  74. Function IsWaitAllowed : Boolean;
  75. begin
  76. IsWaitAllowed:=GlobalIsThreadBlockable<>0;
  77. end;
  78. Function GetClockTime: TOSTime;
  79. var
  80. NanoSecsPast: TOSTime;
  81. begin
  82. if __wasi_clock_time_get(__WASI_CLOCKID_REALTIME,1000000,@NanoSecsPast)=__WASI_ERRNO_SUCCESS then
  83. GetClockTime:=NanoSecsPast
  84. else
  85. GetClockTime:=0;
  86. end;
  87. Function GetSelfThread : TThreadID;
  88. begin
  89. GetSelfThread:=GlobalCurrentThread;
  90. end;
  91. Function GetThreadState(aThread : TThreadID) : TThreadState;
  92. begin
  93. GetThreadState:=PWasmThread(aThread)^.State
  94. end;
  95. function WasiInitManager: Boolean;
  96. begin
  97. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Initializing manager');{$ENDIF}
  98. FillChar(MainThread,SizeOf(MainThread),0);
  99. MainThread.State:=tsRunning;
  100. GlobalIsMainThread:=1;
  101. GlobalIsWorkerThread:=0;
  102. GlobalCurrentThread:=@MainThread;
  103. {$IFDEF FPC_WASM_MAIN_THREAD_CAN_WAIT}
  104. GlobalIsThreadBlockable:=1;
  105. {$ELSE FPC_WASM_MAIN_THREAD_CAN_WAIT}
  106. GlobalIsThreadBlockable:=0;
  107. {$ENDIF FPC_WASM_MAIN_THREAD_CAN_WAIT}
  108. InitMutex(TWasmMutex(InitialHeapCriticalSection));
  109. InitialHeapCriticalSectionInitialized:=true;
  110. if TLSInfoBlock=Nil then
  111. TLSInfoBlock:=AllocateOSInfoBlock;
  112. {$IFDEF DEBUGWASMTHREADS}
  113. if TLSInfoBlock = Nil then
  114. DebugWriteln('Initializing manager done: failed');
  115. {$ENDIF}
  116. WasiInitManager:=True;
  117. end;
  118. function WasiDoneManager: Boolean;
  119. begin
  120. WasiDoneManager:=True;
  121. end;
  122. { ----------------------------------------------------------------------
  123. Critical section (mutex)
  124. ----------------------------------------------------------------------}
  125. procedure WasiInitCriticalSection(var cs);
  126. begin
  127. InitMutex(TWasmMutex(CS));
  128. end;
  129. procedure WasiDoneCriticalSection(var cs);
  130. begin
  131. DoneMutex(TWasmMutex(CS));
  132. end;
  133. procedure WasiEnterCriticalSection(var cs);
  134. begin
  135. LockMutex(TWasmMutex(CS));
  136. end;
  137. function WasiCriticalSectionTryEnter(var cs):longint;
  138. begin
  139. WasiCriticalSectionTryEnter:=Ord(TryLockMutex(TWasmMutex(CS)))
  140. end;
  141. procedure WasiLeaveCriticalSection(var cs);
  142. begin
  143. UnLockMutex(TWasmMutex(CS));
  144. end;
  145. { ----------------------------------------------------------------------
  146. RTL event
  147. ----------------------------------------------------------------------}
  148. function WasiRTLCreateEvent:PRTLEvent;
  149. Var
  150. P : PWasmRTLEvent;
  151. begin
  152. New(P);
  153. fpc_wasm32_i32_atomic_store(@P^.Signal,0);
  154. fpc_wasm32_i32_atomic_store8(@P^.Destroying,0);
  155. Result:=P;
  156. end;
  157. procedure WasiRTLEventSetEvent(AEvent:PRTLEvent);
  158. Var
  159. P : PWasmRTLEvent absolute aEvent;
  160. a : longint;
  161. begin
  162. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : setting signal=1');{$ENDIF}
  163. fpc_wasm32_i32_atomic_store(@P^.Signal,1);
  164. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : notifying waiting threads');{$ENDIF}
  165. a:=fpc_wasm32_memory_atomic_notify(@(P^.Signal),MaxThreadSignal);
  166. end;
  167. procedure WasiRTLEventDestroy(AEvent:PRTLEvent);
  168. Var
  169. P : PWasmRTLEvent absolute aEvent;
  170. begin
  171. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : setting destroying to true');{$ENDIF}
  172. fpc_wasm32_i32_atomic_store8(@P^.Destroying,1);
  173. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : setting event to notify others');{$ENDIF}
  174. WasiRTLEventSetEvent(aEvent);
  175. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : freeing memory');{$ENDIF}
  176. Dispose(P);
  177. end;
  178. procedure WasiRTLEventResetEvent(AEvent:PRTLEvent);
  179. Var
  180. P : PWasmRTLEvent absolute aEvent;
  181. begin
  182. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventResetEvent : setting signal=0');{$ENDIF}
  183. fpc_wasm32_i32_atomic_store(@P^.Signal,0);
  184. end;
  185. procedure WasiRTLEventWaitFor_WaitAllowed(AEvent:PWasmRTLEvent; aTimeoutNs : Int64);
  186. Var
  187. a : Longint;
  188. begin
  189. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : waiting');{$ENDIF}
  190. a:=fpc_wasm32_memory_atomic_wait32(@(aEvent^.Signal),0,aTimeoutNs);
  191. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : done');{$ENDIF}
  192. end;
  193. procedure WasiRTLEventWaitFor_WaitNotAllowed(AEvent:PWasmRTLEvent; aTimeoutNs : Int64);
  194. Var
  195. EndTime : Int64;
  196. IsTimeOut : Boolean;
  197. IsDone : Boolean;
  198. begin
  199. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : waiting');{$ENDIF}
  200. if aTimeoutNs>=0 then
  201. EndTime:=GetClockTime+aTimeoutNs
  202. else
  203. EndTime:=0;
  204. Repeat
  205. IsTimeOut:=(aTimeoutNs>=0) and (GetClockTime>EndTime);
  206. IsDone:=(fpc_wasm32_i32_atomic_load(@aEvent^.Signal)=1) or (fpc_wasm32_i32_atomic_load8_u(@aEvent^.Destroying)<>0);
  207. Until isTimeOut or IsDone;
  208. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : done waiting (isTimeout='+intToStr(Ord(isTimeOut))+',IsDone='+intToStr(Ord(IsDone))+
  209. ',Signal='+IntToStr(aEvent^.Signal)+',Destroying='+IntToStr(Ord(aEvent^.Destroying))+')');{$ENDIF}
  210. end;
  211. procedure WasiRTLEventWaitFor(AEvent:PRTLEvent);
  212. Var
  213. P : PWasmRTLEvent absolute aEvent;
  214. begin
  215. if IsWaitAllowed then
  216. WasiRTLEventWaitFor_WaitAllowed(P,-1)
  217. else
  218. WasiRTLEventWaitFor_WaitNotAllowed(P,-1);
  219. end;
  220. procedure WasiRTLEventWaitForTimeout(AEvent:PRTLEvent;timeout : longint);
  221. Var
  222. P : PWasmRTLEvent absolute aEvent;
  223. TimeoutNs: Int64;
  224. begin
  225. if timeout=-1 then
  226. TimeoutNs:=-1
  227. else
  228. TimeoutNs:=Int64(timeout)*1000000;
  229. if IsWaitAllowed then
  230. WasiRTLEventWaitFor_WaitAllowed(P,TimeoutNs)
  231. else
  232. WasiRTLEventWaitFor_WaitNotAllowed(P,TimeoutNs);
  233. end;
  234. { ----------------------------------------------------------------------
  235. Thread
  236. ----------------------------------------------------------------------}
  237. //procedure FPCWasmThreadSetStackPointer(Address: Pointer); [public, alias: 'FPC_WASM_THREAD_SET_STACK_POINTER'];
  238. //begin
  239. // fpc_wasm32_set_base_pointer(Address);
  240. //end;
  241. // Javascript definition: TThreadInitInstanceFunction = Function(IsWorkerThread : Longint; IsMainThread : Integer; CanBlock : Integer) : Integer;
  242. //Function FPCWasmThreadInit(IsWorkerThread : Longint; IsMainThread : Longint; CanBlock : Longint) : Longint; [public, alias: 'FPC_WASM_THREAD_INIT'];
  243. //
  244. //begin
  245. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadInit('+IntToStr(IsWorkerThread)+','+IntToStr(IsMainThread)+','+IntToStr(CanBlock)+')');{$ENDIF}
  246. // GlobalIsWorkerThread:=IsWorkerThread;
  247. // GlobalIsMainThread:=IsMainThread;
  248. // GlobalIsThreadBlockable:=CanBlock;
  249. // Result:=0;
  250. //end;
  251. procedure WasiAllocateThreadVars; forward;
  252. // Javascript definition: TThreadEntryFunction = Function(ThreadId : Longint; RunFunction : Longint; Args : LongInt) : Longint;
  253. //Function FPCWasmThreadEntry(ThreadID : PWasmThread; RunFunction : Pointer; Args : Pointer) : Longint; [public, alias: 'FPC_WASM_THREAD_ENTRY'];
  254. //begin
  255. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry('+IntToStr(PtrUint(ThreadID))+','+IntToStr(PtrUint(RunFunction))+','+IntToStr(PtrUint(Args))+')');{$ENDIF}
  256. // GlobalCurrentThread:=ThreadID;
  257. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: allocating threadvars (thread function: '+intToStr(PtrUint(RunFunction))+')');{$ENDIF}
  258. // WasiAllocateThreadVars;
  259. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: calling initthread (thread function: '+intToStr(PtrUint(RunFunction))+')');{$ENDIF}
  260. // InitThread;
  261. // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: calling thread function '+intToStr(PtrUint(RunFunction)));{$ENDIF}
  262. // Result:=tthreadfunc(RunFunction)(args);
  263. //end;
  264. {$push}{$S-} // no stack checking for this procedure
  265. procedure FPCWasmThreadStartPascal(tid: longint; start_arg: PWasmThread);
  266. begin
  267. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal(...)');{$ENDIF}
  268. start_arg^.ID:=tid;
  269. GlobalCurrentThread:=@start_arg;
  270. GlobalIsMainThread:=0;
  271. GlobalIsWorkerThread:=1;
  272. {$IFDEF FPC_WASM_WORKER_THREADS_CAN_WAIT}
  273. GlobalIsThreadBlockable:=1;
  274. {$ELSE FPC_WASM_WORKER_THREADS_CAN_WAIT}
  275. GlobalIsThreadBlockable:=0;
  276. {$ENDIF FPC_WASM_WORKER_THREADS_CAN_WAIT}
  277. start_arg^.State:=tsRunning;
  278. InitThread(start_arg^.StackSize);
  279. StackBottom:=start_arg^.StackBlock;
  280. try
  281. start_arg^.ExitCode:=Cardinal(start_arg^.ThreadFunction(start_arg^.ThreadFunctionArg));
  282. except
  283. on e: EWasmThreadTerminate do
  284. begin
  285. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Caught EWasmThreadTerminate with ExitCode='+IntToStr(e.ExitCode));{$ENDIF}
  286. start_arg^.ExitCode:=e.ExitCode;
  287. end;
  288. else
  289. begin
  290. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Uncaught exception');{$ENDIF}
  291. { TODO: what should we return here? }
  292. start_arg^.ExitCode:=High(Cardinal);
  293. end;
  294. end;
  295. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Signaling end of thread');{$ENDIF}
  296. WasiRTLEventSetEvent(start_arg^.DoneEvent);
  297. end;
  298. {$pop}
  299. procedure wasi_thread_start(tid: longint; start_arg: PWasmThread); assembler; nostackframe;
  300. asm
  301. local.get 1 ;; start_arg
  302. i32.load ;; load InitialStackPointer
  303. global.set $__stack_pointer
  304. ;; call fpc_wasm32_init_tls from within assembly code, because in branchful
  305. ;; exceptions mode, Free Pascal generates threadvar access after every
  306. ;; function call. Therefore, we want threadvars to be initialized, before we
  307. ;; call any sort of Pascal code.
  308. local.get 1 ;; start_arg
  309. i32.const 4 ;; offset to InitTLSBase
  310. i32.add
  311. i32.load
  312. call $fpc_wasm32_init_tls
  313. local.get 0 ;; tid
  314. local.get 1 ;; start_arg
  315. call $FPCWasmThreadStartPascal
  316. end;
  317. exports wasi_thread_start;
  318. Function wasi_thread_spawn(start_arg: PWasmThread) : LongInt; external 'wasi' name 'thread-spawn';
  319. function WasiBeginThread(sa : Pointer;stacksize : PtrUInt; ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword; var ThreadId : TThreadID) : TThreadID;
  320. Const
  321. HeapAlignment=16;
  322. Var
  323. T : PWasmThread;
  324. begin
  325. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread(sa: '+IntToStr(PtrUint(Sa))+',ss: '+IntToStr(PtrUint(StackSize))+',TF: '+IntToStr(PtrUint(ThreadFunction))+',Arg: '+IntToStr(PtrUint(P))+',fl: '+IntToStr(PtrUint(CreationFlags))+',ID: '+IntToStr(PtrUint(ThreadID))+')');{$ENDIF}
  326. New(T);
  327. T^.StackBlock:=nil;
  328. T^.TLSBlock:=nil;
  329. ThreadID:=T;
  330. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread thread ID : '+IntToStr(PtrUint(ThreadID)));{$ENDIF}
  331. T^.ThreadFunction:=ThreadFunction;
  332. T^.ThreadFunctionArg:=p;
  333. if stacksize<=0 then
  334. stacksize:=StkLen;
  335. T^.StackSize:=stacksize;
  336. T^.StackBlock:=GetMem(stacksize);
  337. T^.InitialStackPointer:=Pointer(PtrUInt(PtrUInt(T^.StackBlock)+stacksize) and $FFFFFFF0);
  338. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: InitialStackPointer='+IntToStr(PtrUint(T^.InitialStackPointer)));{$ENDIF}
  339. T^.TLSBlock:=AllocMem(fpc_wasm32_tls_size+fpc_wasm32_tls_align-1);
  340. T^.InitTLSBase:=Align(T^.TLSBlock,fpc_wasm32_tls_align);
  341. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: InitTLSBase='+IntToStr(PtrUint(T^.InitTLSBase)));{$ENDIF}
  342. InitMutex(T^.Running,mkNormal);
  343. T^.DoneEvent:=WasiRTLCreateEvent;
  344. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: Locked mutex');{$ENDIF}
  345. if wasi_thread_spawn(T)>0 then
  346. begin
  347. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread OK, setting result');{$ENDIF}
  348. WasiBeginThread:=T;
  349. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread OK, done setting result');{$ENDIF}
  350. end
  351. else
  352. begin
  353. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed');{$ENDIF}
  354. WasiRTLEventDestroy(T^.DoneEvent);
  355. DoneMutex(T^.Running);
  356. if Assigned(T^.StackBlock) then
  357. FreeMem(T^.StackBlock);
  358. if Assigned(T^.TLSBlock) then
  359. FreeMem(T^.TLSBlock);
  360. Dispose(T);
  361. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed, freeing thread struct');{$ENDIF}
  362. WasiBeginThread:=TThreadID(0);
  363. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed, returning 0');{$ENDIF}
  364. end
  365. end;
  366. procedure WasiEndThread(ExitCode : DWord);
  367. Var
  368. T : PWasmThread;
  369. begin
  370. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('EndThread('+IntToStr(ExitCode)+')');{$ENDIF}
  371. raise EWasmThreadTerminate.Create(ExitCode);
  372. end;
  373. function WasiSuspendThread(threadHandle : TThreadID) : dword;
  374. begin
  375. WasiSuspendThread:=DWord(-1);
  376. end;
  377. function WasiResumeThread(threadHandle : TThreadID) : dword;
  378. begin
  379. WasiResumeThread:=DWord(-1);
  380. end;
  381. function WasiKillThread(threadHandle : TThreadID) : dword;
  382. begin
  383. WasiKillThread:=DWord(-1);
  384. end;
  385. function WasiCloseThread(threadHandle : TThreadID) : dword;
  386. begin
  387. Result:=0;
  388. end;
  389. procedure WasiThreadSwitch;
  390. begin
  391. // Normally a yield, but this does not (yet) exist in webassembly.
  392. end;
  393. function WasiWaitForThreadTerminate(threadHandle : TThreadID; TimeoutMs : longint) : dword;
  394. Var
  395. Res : LongInt;
  396. TH : PWasmThread absolute ThreadHandle;
  397. TimeoutNs : Int64;
  398. begin
  399. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+','+IntToStr(TimeoutMs)+')');{$ENDIF}
  400. if TimeoutMs>=0 then
  401. TimeoutNs:=TimeoutMs*1000000
  402. else
  403. TimeoutNs:=-1;
  404. WasiRTLEventWaitFor(TH^.DoneEvent);
  405. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Event set, waiting for lock');{$ENDIF}
  406. Case LockMuTexTimeout(PWasmThread(ThreadHandle)^.Running,TimeoutNs) of
  407. lmrOK : Res:=LongInt(TH^.ExitCode);
  408. lmrError : Res:=-2;
  409. else
  410. Res:=-1;
  411. end;
  412. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Got Lock');{$ENDIF}
  413. UnLockMuTex(PWasmThread(ThreadHandle)^.Running);
  414. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Lock released');{$ENDIF}
  415. WasiWaitForThreadTerminate:=DWord(Res);
  416. end;
  417. function WasiThreadSetPriority(threadHandle : TThreadID; Prio: longint): boolean;
  418. begin
  419. Result:=False;
  420. end;
  421. function WasiThreadGetPriority(threadHandle : TThreadID): longint;
  422. begin
  423. Result:=0;
  424. end;
  425. function WasiGetCurrentThreadId : TThreadID;
  426. begin
  427. Result:=GetSelfThread;
  428. end;
  429. procedure WasiThreadSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
  430. Var
  431. P : PWasmThread absolute ThreadHandle;
  432. Len : Integer;
  433. begin
  434. Len:=Length(ThreadName);
  435. SetLength(P^.ThreadName,Len);
  436. if Len>0 then
  437. Move(ThreadName[1],P^.ThreadName[0],Len);
  438. end;
  439. {$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
  440. procedure WasiThreadSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
  441. Var
  442. P : PWasmThread absolute ThreadHandle;
  443. LThreadName : RawBytestring;
  444. Len : Integer;
  445. begin
  446. Len:=Length(LThreadName);
  447. LThreadName:=Utf8Encode(ThreadName);
  448. SetLength(P^.ThreadName,Len*SizeOf(UnicodeChar));
  449. if Len>0 then
  450. Move(LThreadName[1],P^.ThreadName[0],Len*SizeOf(UnicodeChar));
  451. end;
  452. {$endif FPC_HAS_FEATURE_UNICODESTRINGS}
  453. { ----------------------------------------------------------------------
  454. Threadvars
  455. ----------------------------------------------------------------------}
  456. Var
  457. threadvarblocksize : PtrUint;
  458. procedure WasiInitThreadVar(var offset : dword;size : dword);
  459. begin
  460. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiInitThreadVar('+IntToStr(offset)+','+IntToStr(size)+')');{$ENDIF}
  461. threadvarblocksize:=align(threadvarblocksize, fpc_wasm32_tls_align);
  462. offset:=threadvarblocksize;
  463. inc(threadvarblocksize,size);
  464. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Done WasiInitThreadVar. Total size: '+IntToStr(threadvarblocksize));{$ENDIF}
  465. end;
  466. procedure WasiAllocateThreadVars;
  467. var
  468. tlsMemBlock : pointer;
  469. tlsBlockSize : Integer;
  470. P : POSMemBlock;
  471. begin
  472. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiAllocateThreadVars');{$ENDIF}
  473. tlsBlockSize:=fpc_wasm32_tls_size;
  474. if threadvarblocksize<>tlsBlocksize then
  475. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Warning : block sizes differ: (linker) '+IntToStr(tlsBlocksize)+'<>'+IntToStr(threadvarblocksize)+' (calculated) !');{$ENDIF}
  476. P:=GetFreeOSBlock;
  477. FillChar((P^.Data)^.TLSMemory,tlsBlockSize,0);
  478. fpc_wasm32_init_tls(@((P^.Data)^.TLSMemory));
  479. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Done WasiAllocateThreadVars');{$ENDIF}
  480. end;
  481. Function GetTLSMemory : Pointer;
  482. begin
  483. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('GetTLSMemory Enter');{$ENDIF}
  484. GetTLSMemory:=fpc_wasm32_tls_base();
  485. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('GetTLSMemory exit: '+InttoStr(PtrUint(fpc_wasm32_tls_base())));{$ENDIF}
  486. end;
  487. procedure WasiReleaseThreadVars;
  488. Var
  489. PTLS : PTLSMem;
  490. begin
  491. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiReleaseThreadVars');{$ENDIF}
  492. PTLS:=GetTLSMemory-Sizeof(Pointer);
  493. ReleaseOSBlock(PTLS^.OSMemBlock);
  494. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiReleaseThreadVars done');{$ENDIF}
  495. end;
  496. procedure HookThread;
  497. { Set up externally created thread }
  498. begin
  499. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('HookThread');{$ENDIF}
  500. WasiAllocateThreadVars;
  501. InitThread(1000000000);
  502. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('HookThread done');{$ENDIF}
  503. end;
  504. function WasiRelocateThreadVar(offset : dword) : pointer;
  505. var
  506. P : Pointer;
  507. begin
  508. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRelocateThreadVar ('+IntToStr(offset)+')');{$ENDIF}
  509. P:=GetTLSMemory;
  510. if (P=Nil) then
  511. begin
  512. HookThread;
  513. P:=GetTLSMemory;
  514. end;
  515. WasiRelocateThreadvar:=P+Offset;
  516. {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRelocateThreadVar done. Result: '+IntToStr(PtrUint(P+Offset)));{$ENDIF}
  517. end;
  518. { ----------------------------------------------------------------------
  519. Basic event
  520. ----------------------------------------------------------------------}
  521. function WasiBasicEventCreate(EventAttributes :Pointer; AManualReset,InitialState : Boolean;const Name:ansistring):pEventState;
  522. begin
  523. {todo:implement}
  524. end;
  525. procedure WasiBasicEventDestroy(state:peventstate);
  526. begin
  527. {todo:implement}
  528. end;
  529. procedure WasiBasicEventResetEvent(state:peventstate);
  530. begin
  531. {todo:implement}
  532. end;
  533. procedure WasiBasicEventSetEvent(state:peventstate);
  534. begin
  535. {todo:implement}
  536. end;
  537. function WasiBasicEventWaitFor(timeout:cardinal;state:peventstate;FUseComWait : Boolean=False):longint;
  538. begin
  539. {todo:implement}
  540. end;
  541. procedure InitSystemThreads;public name '_FPC_InitSystemThreads';
  542. begin
  543. with WasiThreadManager do
  544. begin
  545. InitManager := @WasiInitManager;
  546. DoneManager := @WasiDoneManager;
  547. BeginThread := @WasiBeginThread;
  548. EndThread := @WasiEndThread;
  549. SuspendThread := @WasiSuspendThread;
  550. ResumeThread := @WasiResumeThread;
  551. KillThread := @WasiKillThread;
  552. CloseThread := @WasiCloseThread;
  553. ThreadSwitch := @WasiThreadSwitch;
  554. WaitForThreadTerminate := @WasiWaitForThreadTerminate;
  555. ThreadSetPriority := @WasiThreadSetPriority;
  556. ThreadGetPriority := @WasiThreadGetPriority;
  557. GetCurrentThreadId := @WasiGetCurrentThreadId;
  558. SetThreadDebugNameA := @WasiThreadSetThreadDebugNameA;
  559. {$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
  560. SetThreadDebugNameU := @WasiThreadSetThreadDebugNameU;
  561. {$endif FPC_HAS_FEATURE_UNICODESTRINGS}
  562. InitCriticalSection := @WasiInitCriticalSection;
  563. DoneCriticalSection := @WasiDoneCriticalSection;
  564. EnterCriticalSection := @WasiEnterCriticalSection;
  565. TryEnterCriticalSection:= @WasiCriticalSectionTryEnter;
  566. LeaveCriticalSection := @WasiLeaveCriticalSection;
  567. InitThreadVar := @WasiInitThreadVar;
  568. RelocateThreadVar := @WasiRelocateThreadVar;
  569. AllocateThreadVars := @WasiAllocateThreadVars;
  570. ReleaseThreadVars := @WasiReleaseThreadVars;
  571. BasicEventCreate := @WasiBasicEventCreate;
  572. BasicEventDestroy := @WasiBasicEventDestroy;
  573. BasicEventResetEvent := @WasiBasicEventResetEvent;
  574. BasicEventSetEvent := @WasiBasicEventSetEvent;
  575. BasiceventWaitFOr := @WasiBasicEventWaitFor;
  576. RTLEventCreate := @WasiRTLCreateEvent;
  577. RTLEventDestroy := @WasiRTLEventDestroy;
  578. RTLEventSetEvent := @WasiRTLEventSetEvent;
  579. RTLEventResetEvent := @WasiRTLEventResetEvent;
  580. RTLEventWaitFor := @WasiRTLEventWaitFor;
  581. RTLEventWaitForTimeout := @WasiRTLEventWaitForTimeout;
  582. end;
  583. SetThreadManager(WasiThreadManager);
  584. end;