|
@@ -1,3 +1,4 @@
|
|
|
|
+{%MainUnit system.pp}
|
|
{
|
|
{
|
|
This file is part of the Free Pascal run time library.
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2022 by Nikolay Nikolov,
|
|
Copyright (c) 2022 by Nikolay Nikolov,
|
|
@@ -18,185 +19,536 @@
|
|
{$fatal This file shouldn't be included if thread support is disabled!}
|
|
{$fatal This file shouldn't be included if thread support is disabled!}
|
|
{$endif FPC_WASM_THREADS}
|
|
{$endif FPC_WASM_THREADS}
|
|
|
|
|
|
-var
|
|
|
|
|
|
+{$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;
|
|
|
|
+ mutex: TWasmMutex;
|
|
|
|
+ Destroying : Boolean;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ PWasmThread = ^TWasmThread;
|
|
|
|
+ TWasmThread = Record
|
|
|
|
+ ID : LongInt; // Allocated by host javascript code
|
|
|
|
+ State : TThreadState;
|
|
|
|
+ DoneEvent : PWasmRTLEvent;
|
|
|
|
+ Running : TWasmMutex;
|
|
|
|
+ ExitCode : Cardinal;
|
|
|
|
+ ThreadName : Array of byte; // UTF8 name
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ MainThread : TWasmThread;
|
|
WasiThreadManager : TThreadManager;
|
|
WasiThreadManager : TThreadManager;
|
|
|
|
+ GlobalIsWorkerThread : Longint; section 'WebAssembly.Global';
|
|
|
|
+ GlobalIsMainThread : Longint; section 'WebAssembly.Global';
|
|
|
|
+ GlobalIsThreadBlockable : Longint; section 'WebAssembly.Global';
|
|
|
|
+ GlobalCurrentThread : PWasmThread; section 'WebAssembly.Global';
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 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;
|
|
function WasiInitManager: Boolean;
|
|
begin
|
|
begin
|
|
- Result:=True;
|
|
|
|
|
|
+ DebugWriteln('Initializing manager');
|
|
|
|
+ if TLSInfoBlock=Nil then
|
|
|
|
+ TLSInfoBlock:=AllocateOSInfoBlock;
|
|
|
|
+ if TLSInfoBlock = Nil then
|
|
|
|
+ DebugWriteln('Initializing manager done: failed');
|
|
|
|
+ WasiInitManager:=True;
|
|
end;
|
|
end;
|
|
|
|
|
|
function WasiDoneManager: Boolean;
|
|
function WasiDoneManager: Boolean;
|
|
begin
|
|
begin
|
|
- Result:=True;
|
|
|
|
|
|
+ WasiDoneManager:=True;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiBeginThread(sa : Pointer;stacksize : PtrUInt; ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword; var ThreadId : TThreadID) : TThreadID;
|
|
|
|
|
|
+{ ----------------------------------------------------------------------
|
|
|
|
+ Critical section (mutex)
|
|
|
|
+ ----------------------------------------------------------------------}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure WasiInitCriticalSection(var cs);
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ InitMutex(TWasmMutex(CS));
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiEndThread(ExitCode : DWord);
|
|
|
|
|
|
+procedure WasiDoneCriticalSection(var cs);
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ DoneMutex(TWasmMutex(CS));
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiSuspendThread(threadHandle : TThreadID) : dword;
|
|
|
|
|
|
+procedure WasiEnterCriticalSection(var cs);
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ LockMutex(TWasmMutex(CS));
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiResumeThread(threadHandle : TThreadID) : dword;
|
|
|
|
|
|
+function WasiCriticalSectionTryEnter(var cs):longint;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ WasiCriticalSectionTryEnter:=Ord(TryLockMutex(TWasmMutex(CS)))
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiKillThread(threadHandle : TThreadID) : dword;
|
|
|
|
|
|
+procedure WasiLeaveCriticalSection(var cs);
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ UnLockMutex(TWasmMutex(CS));
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiCloseThread(threadHandle : TThreadID) : dword;
|
|
|
|
|
|
+{ ----------------------------------------------------------------------
|
|
|
|
+ RTL event
|
|
|
|
+ ----------------------------------------------------------------------}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+function WasiRTLCreateEvent:PRTLEvent;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ New(P);
|
|
|
|
+ P^.Signal:=0;
|
|
|
|
+ P^.Destroying:=False;
|
|
|
|
+ InitMutex(P^.Mutex);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiThreadSwitch;
|
|
|
|
|
|
+procedure WasiRTLEventSetEvent(AEvent:PRTLEvent);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent absolute aEvent;
|
|
|
|
+ a : longint;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : locking mutex');{$ENDIF}
|
|
|
|
+ LockMutex(P^.Mutex);
|
|
|
|
+ P^.Signal:=1;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : unlocking mutex');{$ENDIF}
|
|
|
|
+ UnLockMutex(P^.Mutex);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventSetEvent : send signal');{$ENDIF}
|
|
|
|
+ a:=fpc_wasm32_memory_atomic_notify(@(P^.Signal),MaxThreadSignal);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiWaitForThreadTerminate(threadHandle : TThreadID; TimeoutMs : longint) : dword;
|
|
|
|
|
|
+procedure WasiRTLEventDestroy(AEvent:PRTLEvent);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent absolute aEvent;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : locking mutex');{$ENDIF}
|
|
|
|
+ LockMutex(P^.Mutex);
|
|
|
|
+ P^.Destroying:=True;
|
|
|
|
+ UnlockMutex(P^.Mutex);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : setting event to notify others');{$ENDIF}
|
|
|
|
+ WasiRTLEventSetEvent(aEvent);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventDestroy : set event to notify others');{$ENDIF}
|
|
|
|
+ FreeMem(P);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiThreadSetPriority(threadHandle : TThreadID; Prio: longint): boolean;
|
|
|
|
|
|
+
|
|
|
|
+procedure WasiRTLEventResetEvent(AEvent:PRTLEvent);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent absolute aEvent;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventResetEvent : locking mutex');{$ENDIF}
|
|
|
|
+ LockMutex(P^.Mutex);
|
|
|
|
+ P^.Signal:=0;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventResetEvent : unlocking mutex');{$ENDIF}
|
|
|
|
+ UnLockMutex(P^.Mutex);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiThreadGetPriority(threadHandle : TThreadID): longint;
|
|
|
|
|
|
+procedure WasiRTLEventWaitFor_WaitAllowed(AEvent:PWasmRTLEvent; aTimeoutMs : Longint);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ a : Longint;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : waiting');{$ENDIF}
|
|
|
|
+ a:=fpc_wasm32_memory_atomic_wait32(@(aEvent^.Signal),1,aTimeoutMs*1000);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitAllowed : done');{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiGetCurrentThreadId : TThreadID;
|
|
|
|
|
|
+
|
|
|
|
+procedure WasiRTLEventWaitFor_WaitNotAllowed(AEvent:PWasmRTLEvent; aTimeoutMs : Longint);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ EndTime : Int64;
|
|
|
|
+ IsTimeOut : Boolean;
|
|
|
|
+ IsDone : Boolean;
|
|
|
|
+ isMain : Boolean;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ IsMain:=GlobalIsMainThread<>0;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : waiting (is main: '+intToStr(Ord(IsMain))+')');{$ENDIF}
|
|
|
|
+ EndTime:=GetClockTime+aTimeoutMs*1000;
|
|
|
|
+ Repeat
|
|
|
|
+ IsTimeOut:=(aTimeOutMS<>0) and (GetClockTime>EndTime);
|
|
|
|
+ IsDone:=(aEvent^.Signal=1) or (aEvent^.Destroying) or (Not IsMain and (GetThreadState(GetSelfThread)<>tsRunning));
|
|
|
|
+ Until isTimeOut or IsDone;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiRTLEventWaitFor_WaitNotAllowed : done waiting');{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiThreadSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
|
|
|
|
|
|
+procedure WasiRTLEventWaitFor(AEvent:PRTLEvent);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent absolute aEvent;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ if IsWaitAllowed then
|
|
|
|
+ WasiRTLEventWaitFor_WaitAllowed(P,0)
|
|
|
|
+ else
|
|
|
|
+ WasiRTLEventWaitFor_WaitNotAllowed(P,0);
|
|
end;
|
|
end;
|
|
-{$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
|
|
|
|
-procedure WasiThreadSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
|
|
|
|
|
|
+
|
|
|
|
+procedure WasiRTLEventWaitForTimeout(AEvent:PRTLEvent;timeout : longint);
|
|
|
|
+Var
|
|
|
|
+ P : PWasmRTLEvent absolute aEvent;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ if IsWaitAllowed then
|
|
|
|
+ WasiRTLEventWaitFor_WaitAllowed(P,TimeOut)
|
|
|
|
+ else
|
|
|
|
+ WasiRTLEventWaitFor_WaitNotAllowed(P,TimeOut);
|
|
end;
|
|
end;
|
|
-{$endif FPC_HAS_FEATURE_UNICODESTRINGS}
|
|
|
|
|
|
|
|
-procedure WasiInitCriticalSection(var cs);
|
|
|
|
|
|
+
|
|
|
|
+{ ----------------------------------------------------------------------
|
|
|
|
+ Thread
|
|
|
|
+ ----------------------------------------------------------------------}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 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
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('FPCWasmThreadInit('+IntToStr(IsWorkerThread)+','+IntToStr(IsMainThread)+','+IntToStr(CanBlock)+')');{$ENDIF}
|
|
|
|
+ GlobalIsWorkerThread:=IsWorkerThread;
|
|
|
|
+ GlobalIsMainThread:=IsMainThread;
|
|
|
|
+ GlobalIsThreadBlockable:=CanBlock;
|
|
|
|
+ Result:=0;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiDoneCriticalSection(var cs);
|
|
|
|
|
|
+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
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$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;
|
|
end;
|
|
|
|
|
|
-procedure WasiEnterCriticalSection(var cs);
|
|
|
|
|
|
+exports FPCWasmThreadInit, FPCWasmThreadEntry;
|
|
|
|
+
|
|
|
|
+Function thread_spawn(thread_id : PInteger; attrs: Pointer; thread_start_func : Pointer; args : Pointer) : LongInt; external 'FPCThreading' name 'thread_spawn';
|
|
|
|
+
|
|
|
|
+function WasiBeginThread(sa : Pointer;stacksize : PtrUInt; ThreadFunction : tthreadfunc;p : pointer;creationFlags : dword; var ThreadId : TThreadID) : TThreadID;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ T : PWasmThread;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$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}
|
|
|
|
+ T:=GetMem(SizeOf(TWasmThread));
|
|
|
|
+ ThreadID:=T;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread thread ID : '+IntToStr(PtrUint(ThreadID)));{$ENDIF}
|
|
|
|
+ InitMutex(T^.Running,mkNormal);
|
|
|
|
+ T^.DoneEvent:=WasiRTLCreateEvent;
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WasiBeginThread: Locked mutex');{$ENDIF}
|
|
|
|
+ if thread_spawn(@(T^.ID),Nil,ThreadFunction,P)=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}
|
|
|
|
+ FreeMem(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;
|
|
end;
|
|
|
|
|
|
-function WasiCriticalSectionTryEnter(var cs):longint;
|
|
|
|
|
|
+procedure WasiEndThread(ExitCode : DWord);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ T : PWasmThread;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('EndThread('+IntToStr(ExitCode)+')');{$ENDIF}
|
|
|
|
+ T:=PWasmThread(GetSelfThread);
|
|
|
|
+ T^.ExitCode:=ExitCode;
|
|
|
|
+ // Signal that we're done
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('EndThread: Signaling end of thread');{$ENDIF}
|
|
|
|
+ WasiRTLEventSetEvent(T^.DoneEvent);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('EndThread: Unlocking mutex');{$ENDIF}
|
|
|
|
+ // Now unlock running mutex
|
|
|
|
+ UnlockMutex(T^.Running);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiLeaveCriticalSection(var cs);
|
|
|
|
|
|
+function WasiSuspendThread(threadHandle : TThreadID) : dword;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ WasiSuspendThread:=DWord(-1);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiInitThreadVar(var offset : dword;size : dword);
|
|
|
|
|
|
+function WasiResumeThread(threadHandle : TThreadID) : dword;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ WasiResumeThread:=DWord(-1);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiRelocateThreadVar(offset : dword) : pointer;
|
|
|
|
|
|
+function WasiKillThread(threadHandle : TThreadID) : dword;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ WasiKillThread:=DWord(-1);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiAllocateThreadVars;
|
|
|
|
|
|
+function WasiCloseThread(threadHandle : TThreadID) : dword;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ Result:=0;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiReleaseThreadVars;
|
|
|
|
|
|
+procedure WasiThreadSwitch;
|
|
begin
|
|
begin
|
|
|
|
+ // Normally a yield, but this does not (yet) exist in webassembly.
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiBasicEventCreate(EventAttributes :Pointer; AManualReset,InitialState : Boolean;const Name:ansistring):pEventState;
|
|
|
|
|
|
+function WasiWaitForThreadTerminate(threadHandle : TThreadID; TimeoutMs : longint) : dword;
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ Res : Integer;
|
|
|
|
+ TH : PWasmThread absolute ThreadHandle;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+','+IntToStr(TimeoutMs)+')');{$ENDIF}
|
|
|
|
+ WasiRTLEventWaitFor(TH^.DoneEvent);
|
|
|
|
+ {$IFDEF DEBUGWASMTHREADS}DebugWriteln('WaitForThreadTerminate('+IntToStr(PtrUINT(TH))+') : Event set, waiting for lock');{$ENDIF}
|
|
|
|
+ Case LockMuTexTimeout(PWasmThread(ThreadHandle)^.Running,TimeoutMS) of
|
|
|
|
+ lmrOK : Res:=0;
|
|
|
|
+ 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;
|
|
end;
|
|
|
|
|
|
-procedure WasiBasicEventDestroy(state:peventstate);
|
|
|
|
|
|
+function WasiThreadSetPriority(threadHandle : TThreadID; Prio: longint): boolean;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ Result:=False;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiBasicEventResetEvent(state:peventstate);
|
|
|
|
|
|
+function WasiThreadGetPriority(threadHandle : TThreadID): longint;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ Result:=0;
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiBasicEventSetEvent(state:peventstate);
|
|
|
|
|
|
+function WasiGetCurrentThreadId : TThreadID;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ Result:=GetSelfThread;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiBasicEventWaitFor(timeout:cardinal;state:peventstate;FUseComWait : Boolean=False):longint;
|
|
|
|
|
|
+procedure WasiThreadSetThreadDebugNameA(threadHandle: TThreadID; const ThreadName: AnsiString);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmThread absolute ThreadHandle;
|
|
|
|
+ Len : Integer;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ Len:=Length(ThreadName);
|
|
|
|
+ SetLength(P^.ThreadName,Len);
|
|
|
|
+ if Len>0 then
|
|
|
|
+ Move(ThreadName[1],P^.ThreadName[0],Len);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function WasiRTLCreateEvent:PRTLEvent;
|
|
|
|
|
|
+{$ifdef FPC_HAS_FEATURE_UNICODESTRINGS}
|
|
|
|
+procedure WasiThreadSetThreadDebugNameU(threadHandle: TThreadID; const ThreadName: UnicodeString);
|
|
|
|
+
|
|
|
|
+Var
|
|
|
|
+ P : PWasmThread absolute ThreadHandle;
|
|
|
|
+ LThreadName : RawBytestring;
|
|
|
|
+ Len : Integer;
|
|
begin
|
|
begin
|
|
- {todo:implement}
|
|
|
|
|
|
+ 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;
|
|
end;
|
|
|
|
+{$endif FPC_HAS_FEATURE_UNICODESTRINGS}
|
|
|
|
|
|
-procedure WasiRTLEventDestroy(AEvent:PRTLEvent);
|
|
|
|
|
|
+
|
|
|
|
+{ ----------------------------------------------------------------------
|
|
|
|
+ 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
|
|
begin
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiRTLEventSetEvent(AEvent:PRTLEvent);
|
|
|
|
|
|
+procedure WasiBasicEventDestroy(state:peventstate);
|
|
begin
|
|
begin
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiRTLEventResetEvent(AEvent:PRTLEvent);
|
|
|
|
|
|
+procedure WasiBasicEventResetEvent(state:peventstate);
|
|
begin
|
|
begin
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiRTLEventWaitFor(AEvent:PRTLEvent);
|
|
|
|
|
|
+procedure WasiBasicEventSetEvent(state:peventstate);
|
|
begin
|
|
begin
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure WasiRTLEventWaitForTimeout(AEvent:PRTLEvent;timeout : longint);
|
|
|
|
|
|
+function WasiBasicEventWaitFor(timeout:cardinal;state:peventstate;FUseComWait : Boolean=False):longint;
|
|
begin
|
|
begin
|
|
{todo:implement}
|
|
{todo:implement}
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+
|
|
procedure InitSystemThreads;public name '_FPC_InitSystemThreads';
|
|
procedure InitSystemThreads;public name '_FPC_InitSystemThreads';
|
|
begin
|
|
begin
|
|
with WasiThreadManager do
|
|
with WasiThreadManager do
|