123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715 |
- {%MainUnit system.pp}
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 2022 by Nikolay Nikolov,
- member of the Free Pascal development team.
- WASI threading support implementation
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$ifndef FPC_WASM_THREADS}
- {$fatal This file shouldn't be included if thread support is disabled!}
- {$endif FPC_WASM_THREADS}
- {$DEFINE FPC_WASM_MAIN_THREAD_CAN_WAIT}
- {$UNDEF FPC_WASM_WORKER_THREADS_CAN_WAIT}
- {//$DEFINE DEBUGWASMTHREADS}
- Const
- MaxThreadSignal = 1000; // maximum threads to signal
- Type
- TThreadState = (tsNone,tsInit,tsRunning,tsCanceling,tsExit);
- TOSTime = __wasi_timestamp_t;
- // Forwards used in mutex
- Function GetClockTime: TOSTime; forward;
- Function IsWaitAllowed : Boolean; forward;
- Function GetSelfThread : TThreadID; forward;
- Function GetThreadState(aThread : TThreadID) : TThreadState; forward;
- {$i wasmmem.inc}
- {$i wasmmutex.inc}
- Type
- PWasmRTLEvent = ^TWasmRTLEvent;
- TWasmRTLEvent = record
- Signal : Longint;
- Destroying : Boolean;
- end;
- PWasmThread = ^TWasmThread;
- TWasmThread = Record
- InitialStackPointer : Pointer;
- InitTLSBase : Pointer;
- ID : LongInt; // Allocated by host
- ThreadFunction : TThreadFunc;
- ThreadFunctionArg : Pointer;
- State : TThreadState;
- DoneEvent : PWasmRTLEvent;
- Running : TWasmMutex;
- ExitCode : Cardinal;
- StackBlock : Pointer;
- TLSBlock : Pointer;
- StackSize : PtrUInt;
- ThreadName : Array of byte; // UTF8 name
- end;
- { EWasmThreadTerminate }
- EWasmThreadTerminate = class(TObject)
- strict private
- FExitCode : DWord;
- public
- constructor Create(AExitCode: DWord);
- property ExitCode: DWord read FExitCode;
- end;
- Var
- MainThread : TWasmThread;
- WasiThreadManager : TThreadManager;
- GlobalIsWorkerThread : Longint; section 'WebAssembly.Global';
- GlobalIsMainThread : Longint; section 'WebAssembly.Global';
- GlobalIsThreadBlockable : Longint; section 'WebAssembly.Global';
- GlobalCurrentThread : PWasmThread; section 'WebAssembly.Global';
- { EWasmThreadTerminate }
- constructor EWasmThreadTerminate.Create(AExitCode: DWord);
- begin
- FExitCode:=AExitCode;
- end;
- // Forward functions
- Function IsWaitAllowed : Boolean;
- begin
- IsWaitAllowed:=GlobalIsThreadBlockable<>0;
- end;
- Function GetClockTime: TOSTime;
- var
- NanoSecsPast: TOSTime;
- begin
- if __wasi_clock_time_get(__WASI_CLOCKID_REALTIME,1000000,@NanoSecsPast)=__WASI_ERRNO_SUCCESS then
- GetClockTime:=NanoSecsPast
- else
- GetClockTime:=0;
- end;
- Function GetSelfThread : TThreadID;
- begin
- GetSelfThread:=GlobalCurrentThread;
- end;
- Function GetThreadState(aThread : TThreadID) : TThreadState;
- begin
- GetThreadState:=PWasmThread(aThread)^.State
- end;
- function WasiInitManager: Boolean;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Initializing manager');{$ENDIF}
- FillChar(MainThread,SizeOf(MainThread),0);
- MainThread.State:=tsRunning;
- GlobalIsMainThread:=1;
- GlobalIsWorkerThread:=0;
- GlobalCurrentThread:=@MainThread;
- {$IFDEF FPC_WASM_MAIN_THREAD_CAN_WAIT}
- GlobalIsThreadBlockable:=1;
- {$ELSE FPC_WASM_MAIN_THREAD_CAN_WAIT}
- GlobalIsThreadBlockable:=0;
- {$ENDIF FPC_WASM_MAIN_THREAD_CAN_WAIT}
- InitMutex(TWasmMutex(InitialHeapCriticalSection));
- InitialHeapCriticalSectionInitialized:=true;
- if TLSInfoBlock=Nil then
- TLSInfoBlock:=AllocateOSInfoBlock;
- {$IFDEF DEBUGWASMTHREADS}
- if TLSInfoBlock = Nil then
- DebugWriteln('Initializing manager done: failed');
- {$ENDIF}
- WasiInitManager:=True;
- end;
- function WasiDoneManager: Boolean;
- begin
- WasiDoneManager:=True;
- end;
- { ----------------------------------------------------------------------
- Critical section (mutex)
- ----------------------------------------------------------------------}
- procedure WasiInitCriticalSection(var cs);
- begin
- InitMutex(TWasmMutex(CS));
- end;
- procedure WasiDoneCriticalSection(var cs);
- begin
- DoneMutex(TWasmMutex(CS));
- end;
- procedure WasiEnterCriticalSection(var cs);
- begin
- LockMutex(TWasmMutex(CS));
- end;
- function WasiCriticalSectionTryEnter(var cs):longint;
- begin
- WasiCriticalSectionTryEnter:=Ord(TryLockMutex(TWasmMutex(CS)))
- end;
- procedure WasiLeaveCriticalSection(var cs);
- begin
- UnLockMutex(TWasmMutex(CS));
- end;
- { ----------------------------------------------------------------------
- RTL event
- ----------------------------------------------------------------------}
- function WasiRTLCreateEvent:PRTLEvent;
- Var
- P : PWasmRTLEvent;
- begin
- New(P);
- fpc_wasm32_i32_atomic_store(@P^.Signal,0);
- fpc_wasm32_i32_atomic_store8(@P^.Destroying,0);
- Result:=P;
- end;
- procedure WasiRTLEventSetEvent(AEvent:PRTLEvent);
- Var
- P : PWasmRTLEvent absolute aEvent;
- a : longint;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : setting signal=1');{$ENDIF}
- fpc_wasm32_i32_atomic_store(@P^.Signal,1);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : notifying waiting threads');{$ENDIF}
- a:=fpc_wasm32_memory_atomic_notify(@(P^.Signal),MaxThreadSignal);
- end;
- procedure WasiRTLEventDestroy(AEvent:PRTLEvent);
- Var
- P : PWasmRTLEvent absolute aEvent;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : setting destroying to true');{$ENDIF}
- fpc_wasm32_i32_atomic_store8(@P^.Destroying,1);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : setting event to notify others');{$ENDIF}
- WasiRTLEventSetEvent(aEvent);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : freeing memory');{$ENDIF}
- Dispose(P);
- end;
- procedure WasiRTLEventResetEvent(AEvent:PRTLEvent);
- Var
- P : PWasmRTLEvent absolute aEvent;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventResetEvent : setting signal=0');{$ENDIF}
- fpc_wasm32_i32_atomic_store(@P^.Signal,0);
- end;
- procedure WasiRTLEventWaitFor_WaitAllowed(AEvent:PWasmRTLEvent; aTimeoutNs : Int64);
- Var
- a : Longint;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : waiting');{$ENDIF}
- a:=fpc_wasm32_memory_atomic_wait32(@(aEvent^.Signal),0,aTimeoutNs);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : done');{$ENDIF}
- end;
- procedure WasiRTLEventWaitFor_WaitNotAllowed(AEvent:PWasmRTLEvent; aTimeoutNs : Int64);
- Var
- EndTime : Int64;
- IsTimeOut : Boolean;
- IsDone : Boolean;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : waiting');{$ENDIF}
- if aTimeoutNs>=0 then
- EndTime:=GetClockTime+aTimeoutNs
- else
- EndTime:=0;
- Repeat
- IsTimeOut:=(aTimeoutNs>=0) and (GetClockTime>EndTime);
- IsDone:=(fpc_wasm32_i32_atomic_load(@aEvent^.Signal)=1) or (fpc_wasm32_i32_atomic_load8_u(@aEvent^.Destroying)<>0);
- Until isTimeOut or IsDone;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : done waiting (isTimeout='+intToStr(Ord(isTimeOut))+',IsDone='+intToStr(Ord(IsDone))+
- ',Signal='+IntToStr(aEvent^.Signal)+',Destroying='+IntToStr(Ord(aEvent^.Destroying))+')');{$ENDIF}
- end;
- procedure WasiRTLEventWaitFor(AEvent:PRTLEvent);
- Var
- P : PWasmRTLEvent absolute aEvent;
- begin
- if IsWaitAllowed then
- WasiRTLEventWaitFor_WaitAllowed(P,-1)
- else
- WasiRTLEventWaitFor_WaitNotAllowed(P,-1);
- end;
- procedure WasiRTLEventWaitForTimeout(AEvent:PRTLEvent;timeout : longint);
- Var
- P : PWasmRTLEvent absolute aEvent;
- TimeoutNs: Int64;
- begin
- if timeout=-1 then
- TimeoutNs:=-1
- else
- TimeoutNs:=Int64(timeout)*1000000;
- if IsWaitAllowed then
- WasiRTLEventWaitFor_WaitAllowed(P,TimeoutNs)
- else
- WasiRTLEventWaitFor_WaitNotAllowed(P,TimeoutNs);
- end;
- { ----------------------------------------------------------------------
- Thread
- ----------------------------------------------------------------------}
- //procedure FPCWasmThreadSetStackPointer(Address: Pointer); [public, alias: 'FPC_WASM_THREAD_SET_STACK_POINTER'];
- //begin
- // fpc_wasm32_set_base_pointer(Address);
- //end;
- // Javascript definition: TThreadInitInstanceFunction = Function(IsWorkerThread : Longint; IsMainThread : Integer; CanBlock : Integer) : Integer;
- //Function FPCWasmThreadInit(IsWorkerThread : Longint; IsMainThread : Longint; CanBlock : Longint) : Longint; [public, alias: 'FPC_WASM_THREAD_INIT'];
- //
- //begin
- // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadInit('+IntToStr(IsWorkerThread)+','+IntToStr(IsMainThread)+','+IntToStr(CanBlock)+')');{$ENDIF}
- // GlobalIsWorkerThread:=IsWorkerThread;
- // GlobalIsMainThread:=IsMainThread;
- // GlobalIsThreadBlockable:=CanBlock;
- // Result:=0;
- //end;
- procedure WasiAllocateThreadVars; forward;
- // Javascript definition: TThreadEntryFunction = Function(ThreadId : Longint; RunFunction : Longint; Args : LongInt) : Longint;
- //Function FPCWasmThreadEntry(ThreadID : PWasmThread; RunFunction : Pointer; Args : Pointer) : Longint; [public, alias: 'FPC_WASM_THREAD_ENTRY'];
- //begin
- // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry('+IntToStr(PtrUint(ThreadID))+','+IntToStr(PtrUint(RunFunction))+','+IntToStr(PtrUint(Args))+')');{$ENDIF}
- // GlobalCurrentThread:=ThreadID;
- // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: allocating threadvars (thread function: '+intToStr(PtrUint(RunFunction))+')');{$ENDIF}
- // WasiAllocateThreadVars;
- // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: calling initthread (thread function: '+intToStr(PtrUint(RunFunction))+')');{$ENDIF}
- // InitThread;
- // {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadEntry: calling thread function '+intToStr(PtrUint(RunFunction)));{$ENDIF}
- // Result:=tthreadfunc(RunFunction)(args);
- //end;
- {$push}{$S-} // no stack checking for this procedure
- procedure FPCWasmThreadStartPascal(tid: longint; start_arg: PWasmThread);
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal(...)');{$ENDIF}
- start_arg^.ID:=tid;
- GlobalCurrentThread:=@start_arg;
- GlobalIsMainThread:=0;
- GlobalIsWorkerThread:=1;
- {$IFDEF FPC_WASM_WORKER_THREADS_CAN_WAIT}
- GlobalIsThreadBlockable:=1;
- {$ELSE FPC_WASM_WORKER_THREADS_CAN_WAIT}
- GlobalIsThreadBlockable:=0;
- {$ENDIF FPC_WASM_WORKER_THREADS_CAN_WAIT}
- start_arg^.State:=tsRunning;
- InitThread(start_arg^.StackSize);
- StackBottom:=start_arg^.StackBlock;
- try
- start_arg^.ExitCode:=Cardinal(start_arg^.ThreadFunction(start_arg^.ThreadFunctionArg));
- except
- on e: EWasmThreadTerminate do
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Caught EWasmThreadTerminate with ExitCode='+IntToStr(e.ExitCode));{$ENDIF}
- start_arg^.ExitCode:=e.ExitCode;
- end;
- else
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Uncaught exception');{$ENDIF}
- { TODO: what should we return here? }
- start_arg^.ExitCode:=High(Cardinal);
- end;
- end;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadStartPascal: Signaling end of thread');{$ENDIF}
- WasiRTLEventSetEvent(start_arg^.DoneEvent);
- end;
- {$pop}
- procedure wasi_thread_start(tid: longint; start_arg: PWasmThread); assembler; nostackframe;
- asm
- local.get 1 ;; start_arg
- i32.load ;; load InitialStackPointer
- global.set $__stack_pointer
- ;; call fpc_wasm32_init_tls from within assembly code, because in branchful
- ;; exceptions mode, Free Pascal generates threadvar access after every
- ;; function call. Therefore, we want threadvars to be initialized, before we
- ;; call any sort of Pascal code.
- local.get 1 ;; start_arg
- i32.const 4 ;; offset to InitTLSBase
- i32.add
- i32.load
- call $fpc_wasm32_init_tls
- local.get 0 ;; tid
- local.get 1 ;; start_arg
- call $FPCWasmThreadStartPascal
- end;
- exports wasi_thread_start;
- Function wasi_thread_spawn(start_arg: PWasmThread) : LongInt; external 'wasi' name 'thread-spawn';
- function WasiBeginThread(sa : Pointer;stacksize : PtrUInt; ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword; var ThreadId : TThreadID) : TThreadID;
- Const
- HeapAlignment=16;
- Var
- T : PWasmThread;
- begin
- {$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}
- New(T);
- T^.StackBlock:=nil;
- T^.TLSBlock:=nil;
- ThreadID:=T;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread thread ID : '+IntToStr(PtrUint(ThreadID)));{$ENDIF}
- T^.ThreadFunction:=ThreadFunction;
- T^.ThreadFunctionArg:=p;
- if stacksize<=0 then
- stacksize:=StkLen;
- T^.StackSize:=stacksize;
- T^.StackBlock:=GetMem(stacksize);
- T^.InitialStackPointer:=Pointer(PtrUInt(PtrUInt(T^.StackBlock)+stacksize) and $FFFFFFF0);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: InitialStackPointer='+IntToStr(PtrUint(T^.InitialStackPointer)));{$ENDIF}
- T^.TLSBlock:=AllocMem(fpc_wasm32_tls_size+fpc_wasm32_tls_align-1);
- T^.InitTLSBase:=Align(T^.TLSBlock,fpc_wasm32_tls_align);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: InitTLSBase='+IntToStr(PtrUint(T^.InitTLSBase)));{$ENDIF}
- InitMutex(T^.Running,mkNormal);
- T^.DoneEvent:=WasiRTLCreateEvent;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: Locked mutex');{$ENDIF}
- if wasi_thread_spawn(T)>0 then
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread OK, setting result');{$ENDIF}
- WasiBeginThread:=T;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread OK, done setting result');{$ENDIF}
- end
- else
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed');{$ENDIF}
- WasiRTLEventDestroy(T^.DoneEvent);
- DoneMutex(T^.Running);
- if Assigned(T^.StackBlock) then
- FreeMem(T^.StackBlock);
- if Assigned(T^.TLSBlock) then
- FreeMem(T^.TLSBlock);
- Dispose(T);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed, freeing thread struct');{$ENDIF}
- WasiBeginThread:=TThreadID(0);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: spawn thread failed, returning 0');{$ENDIF}
- end
- end;
- procedure WasiEndThread(ExitCode : DWord);
- Var
- T : PWasmThread;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('EndThread('+IntToStr(ExitCode)+')');{$ENDIF}
- raise EWasmThreadTerminate.Create(ExitCode);
- end;
- function WasiSuspendThread(threadHandle : TThreadID) : dword;
- begin
- WasiSuspendThread:=DWord(-1);
- end;
- function WasiResumeThread(threadHandle : TThreadID) : dword;
- begin
- WasiResumeThread:=DWord(-1);
- end;
- function WasiKillThread(threadHandle : TThreadID) : dword;
- begin
- WasiKillThread:=DWord(-1);
- end;
- function WasiCloseThread(threadHandle : TThreadID) : dword;
- begin
- Result:=0;
- end;
- procedure WasiThreadSwitch;
- begin
- // Normally a yield, but this does not (yet) exist in webassembly.
- end;
- function WasiWaitForThreadTerminate(threadHandle : TThreadID; TimeoutMs : longint) : dword;
- Var
- Res : LongInt;
- TH : PWasmThread absolute ThreadHandle;
- TimeoutNs : Int64;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+','+IntToStr(TimeoutMs)+')');{$ENDIF}
- if TimeoutMs>=0 then
- TimeoutNs:=TimeoutMs*1000000
- else
- TimeoutNs:=-1;
- WasiRTLEventWaitFor(TH^.DoneEvent);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Event set, waiting for lock');{$ENDIF}
- Case LockMuTexTimeout(PWasmThread(ThreadHandle)^.Running,TimeoutNs) of
- lmrOK : Res:=LongInt(TH^.ExitCode);
- lmrError : Res:=-2;
- else
- Res:=-1;
- end;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Got Lock');{$ENDIF}
- UnLockMuTex(PWasmThread(ThreadHandle)^.Running);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Lock released');{$ENDIF}
- WasiWaitForThreadTerminate:=DWord(Res);
- end;
- function WasiThreadSetPriority(threadHandle : TThreadID; Prio: longint): boolean;
- begin
- Result:=False;
- end;
- function WasiThreadGetPriority(threadHandle : TThreadID): longint;
- begin
- Result:=0;
- end;
- function WasiGetCurrentThreadId : TThreadID;
- begin
- Result:=GetSelfThread;
- end;
- procedure WasiThreadSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
- Var
- P : PWasmThread absolute ThreadHandle;
- Len : Integer;
- begin
- Len:=Length(ThreadName);
- SetLength(P^.ThreadName,Len);
- if Len>0 then
- Move(ThreadName[1],P^.ThreadName[0],Len);
- end;
- {$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
- procedure WasiThreadSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
- Var
- P : PWasmThread absolute ThreadHandle;
- LThreadName : RawBytestring;
- Len : Integer;
- begin
- Len:=Length(LThreadName);
- LThreadName:=Utf8Encode(ThreadName);
- SetLength(P^.ThreadName,Len*SizeOf(UnicodeChar));
- if Len>0 then
- Move(LThreadName[1],P^.ThreadName[0],Len*SizeOf(UnicodeChar));
- end;
- {$endif FPC_HAS_FEATURE_UNICODESTRINGS}
- { ----------------------------------------------------------------------
- Threadvars
- ----------------------------------------------------------------------}
- Var
- threadvarblocksize : PtrUint;
- procedure WasiInitThreadVar(var offset : dword;size : dword);
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiInitThreadVar('+IntToStr(offset)+','+IntToStr(size)+')');{$ENDIF}
- threadvarblocksize:=align(threadvarblocksize, fpc_wasm32_tls_align);
- offset:=threadvarblocksize;
- inc(threadvarblocksize,size);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Done WasiInitThreadVar. Total size: '+IntToStr(threadvarblocksize));{$ENDIF}
- end;
- procedure WasiAllocateThreadVars;
- var
- tlsMemBlock : pointer;
- tlsBlockSize : Integer;
- P : POSMemBlock;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiAllocateThreadVars');{$ENDIF}
- tlsBlockSize:=fpc_wasm32_tls_size;
- if threadvarblocksize<>tlsBlocksize then
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Warning : block sizes differ: (linker) '+IntToStr(tlsBlocksize)+'<>'+IntToStr(threadvarblocksize)+' (calculated) !');{$ENDIF}
- P:=GetFreeOSBlock;
- FillChar((P^.Data)^.TLSMemory,tlsBlockSize,0);
- fpc_wasm32_init_tls(@((P^.Data)^.TLSMemory));
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('Done WasiAllocateThreadVars');{$ENDIF}
- end;
- Function GetTLSMemory : Pointer;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('GetTLSMemory Enter');{$ENDIF}
- GetTLSMemory:=fpc_wasm32_tls_base();
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('GetTLSMemory exit: '+InttoStr(PtrUint(fpc_wasm32_tls_base())));{$ENDIF}
- end;
- procedure WasiReleaseThreadVars;
- Var
- PTLS : PTLSMem;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiReleaseThreadVars');{$ENDIF}
- PTLS:=GetTLSMemory-Sizeof(Pointer);
- ReleaseOSBlock(PTLS^.OSMemBlock);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiReleaseThreadVars done');{$ENDIF}
- end;
- procedure HookThread;
- { Set up externally created thread }
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('HookThread');{$ENDIF}
- WasiAllocateThreadVars;
- InitThread(1000000000);
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('HookThread done');{$ENDIF}
- end;
- function WasiRelocateThreadVar(offset : dword) : pointer;
- var
- P : Pointer;
- begin
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRelocateThreadVar ('+IntToStr(offset)+')');{$ENDIF}
- P:=GetTLSMemory;
- if (P=Nil) then
- begin
- HookThread;
- P:=GetTLSMemory;
- end;
- WasiRelocateThreadvar:=P+Offset;
- {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRelocateThreadVar done. Result: '+IntToStr(PtrUint(P+Offset)));{$ENDIF}
- end;
- { ----------------------------------------------------------------------
- Basic event
- ----------------------------------------------------------------------}
- function WasiBasicEventCreate(EventAttributes :Pointer; AManualReset,InitialState : Boolean;const Name:ansistring):pEventState;
- begin
- {todo:implement}
- end;
- procedure WasiBasicEventDestroy(state:peventstate);
- begin
- {todo:implement}
- end;
- procedure WasiBasicEventResetEvent(state:peventstate);
- begin
- {todo:implement}
- end;
- procedure WasiBasicEventSetEvent(state:peventstate);
- begin
- {todo:implement}
- end;
- function WasiBasicEventWaitFor(timeout:cardinal;state:peventstate;FUseComWait : Boolean=False):longint;
- begin
- {todo:implement}
- end;
- procedure InitSystemThreads;public name '_FPC_InitSystemThreads';
- begin
- with WasiThreadManager do
- begin
- InitManager := @WasiInitManager;
- DoneManager := @WasiDoneManager;
- BeginThread := @WasiBeginThread;
- EndThread := @WasiEndThread;
- SuspendThread := @WasiSuspendThread;
- ResumeThread := @WasiResumeThread;
- KillThread := @WasiKillThread;
- CloseThread := @WasiCloseThread;
- ThreadSwitch := @WasiThreadSwitch;
- WaitForThreadTerminate := @WasiWaitForThreadTerminate;
- ThreadSetPriority := @WasiThreadSetPriority;
- ThreadGetPriority := @WasiThreadGetPriority;
- GetCurrentThreadId := @WasiGetCurrentThreadId;
- SetThreadDebugNameA := @WasiThreadSetThreadDebugNameA;
- {$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
- SetThreadDebugNameU := @WasiThreadSetThreadDebugNameU;
- {$endif FPC_HAS_FEATURE_UNICODESTRINGS}
- InitCriticalSection := @WasiInitCriticalSection;
- DoneCriticalSection := @WasiDoneCriticalSection;
- EnterCriticalSection := @WasiEnterCriticalSection;
- TryEnterCriticalSection:= @WasiCriticalSectionTryEnter;
- LeaveCriticalSection := @WasiLeaveCriticalSection;
- InitThreadVar := @WasiInitThreadVar;
- RelocateThreadVar := @WasiRelocateThreadVar;
- AllocateThreadVars := @WasiAllocateThreadVars;
- ReleaseThreadVars := @WasiReleaseThreadVars;
- BasicEventCreate := @WasiBasicEventCreate;
- BasicEventDestroy := @WasiBasicEventDestroy;
- BasicEventResetEvent := @WasiBasicEventResetEvent;
- BasicEventSetEvent := @WasiBasicEventSetEvent;
- BasiceventWaitFOr := @WasiBasicEventWaitFor;
- RTLEventCreate := @WasiRTLCreateEvent;
- RTLEventDestroy := @WasiRTLEventDestroy;
- RTLEventSetEvent := @WasiRTLEventSetEvent;
- RTLEventResetEvent := @WasiRTLEventResetEvent;
- RTLEventWaitFor := @WasiRTLEventWaitFor;
- RTLEventWaitForTimeout := @WasiRTLEventWaitForTimeout;
- end;
- SetThreadManager(WasiThreadManager);
- end;
|