mirror of https://github.com/red-prig/fpPS4.git
1230 lines
22 KiB
Plaintext
1230 lines
22 KiB
Plaintext
unit kern_thread;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sysutils,
|
|
mqueue,
|
|
kern_param,
|
|
kern_thr,
|
|
ucontext,
|
|
signal,
|
|
signalvar,
|
|
time,
|
|
rtprio,
|
|
hamt;
|
|
|
|
procedure thread_reap();
|
|
|
|
function thread_alloc(pages:Word):p_kthread;
|
|
procedure thread_free(td:p_kthread);
|
|
|
|
function sys_thr_new (_param:Pointer;_size:Integer):Integer;
|
|
function sys_thr_create (ctx:Pointer;id:PDWORD;flags:Integer):Integer;
|
|
function sys_thr_self (id:PDWORD):Integer;
|
|
procedure sys_thr_exit (state:PDWORD);
|
|
function sys_thr_kill (id,sig:Integer):Integer;
|
|
function sys_thr_kill2 (pid,id,sig:Integer):Integer;
|
|
function sys_thr_suspend (timeout:Pointer):Integer;
|
|
function sys_thr_wake (id:DWORD):Integer;
|
|
function sys_thr_set_name(id:DWORD;pname:PChar):Integer;
|
|
function sys_thr_get_name(id:DWORD;pname:PChar):Integer;
|
|
|
|
function sys_amd64_set_fsbase(base:Pointer):Integer;
|
|
function sys_amd64_get_fsbase(base:PPointer):Integer;
|
|
function sys_amd64_set_gsbase(base:Pointer):Integer;
|
|
function sys_amd64_get_gsbase(base:PPointer):Integer;
|
|
|
|
procedure thread_inc_ref(td:p_kthread);
|
|
procedure thread_dec_ref(td:p_kthread);
|
|
procedure thread_lock_assert(td:p_kthread);
|
|
procedure thread_lock (td:p_kthread);
|
|
procedure thread_unlock (td:p_kthread);
|
|
function tdfind(tid:DWORD):p_kthread;
|
|
|
|
procedure threads_lock;
|
|
function threads_trylock:Boolean;
|
|
procedure threads_unlock;
|
|
|
|
procedure KernSetThreadDebugName(newtd:p_kthread;prefix:PChar);
|
|
|
|
function SIGPENDING(td:p_kthread):Boolean;
|
|
|
|
procedure threadinit; //SYSINIT
|
|
|
|
function kthread_add (func,arg:Pointer;newtdp:pp_kthread;pages:Word;name:PChar):Integer;
|
|
procedure kthread_exit();
|
|
|
|
procedure thread_suspend_all(exclude:p_kthread);
|
|
procedure thread_resume_all (exclude:p_kthread);
|
|
|
|
var
|
|
init_tty_cb:Tprocedure;
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
systm,
|
|
kern_mtx,
|
|
md_context,
|
|
machdep,
|
|
md_thread,
|
|
kern_rwlock,
|
|
kern_sig,
|
|
kern_proc,
|
|
kern_rangelock,
|
|
sched_ule,
|
|
sys_sleepqueue;
|
|
|
|
//
|
|
|
|
procedure umtx_thread_init(td:p_kthread); external;
|
|
procedure umtx_thread_exit(td:p_kthread); external;
|
|
procedure umtx_thread_fini(td:p_kthread); external;
|
|
function kern_umtx_wake (td:p_kthread;umtx:Pointer;n_wake,priv:Integer):Integer; external;
|
|
function umtx_copyin_timeout(addr:Pointer;tsp:p_timespec):Integer; external;
|
|
|
|
procedure jit_ctx_free(td:p_kthread); external;
|
|
|
|
//
|
|
|
|
var
|
|
p_threads :TAILQ_HEAD=(tqh_first:nil;tqh_last:@p_threads.tqh_first);
|
|
p_numthreads:Integer=0;
|
|
|
|
tidhashtbl :TSTUB_HAMT32;
|
|
tidhash_lock:Pointer=nil;
|
|
|
|
zombie_threads:TAILQ_HEAD=(tqh_first:nil;tqh_last:@zombie_threads.tqh_first);
|
|
zombie_lock :Pointer=nil;
|
|
|
|
const
|
|
max_threads_per_proc=1500;
|
|
|
|
function SIGPENDING(td:p_kthread):Boolean; public;
|
|
begin
|
|
Result:=SIGNOTEMPTY(@td^.td_sigqueue.sq_signals) and
|
|
sigsetmasked(@td^.td_sigqueue.sq_signals,@td^.td_sigmask);
|
|
end;
|
|
|
|
function get_p_threads:Pointer; public;
|
|
begin
|
|
Result:=@p_threads;
|
|
end;
|
|
|
|
//
|
|
|
|
function _thread_null(parameter:pointer):ptrint; register;
|
|
begin
|
|
Result:=0;
|
|
end;
|
|
|
|
var
|
|
_t_init:Integer=0;
|
|
|
|
procedure threadinit;
|
|
begin
|
|
if (System.InterlockedExchange(_t_init,1)<>0) then Exit;
|
|
//init internals
|
|
BeginThread(@_thread_null);
|
|
end;
|
|
|
|
{
|
|
* Place an unused thread on the zombie list.
|
|
* Use the slpq as that must be unused by now.
|
|
}
|
|
procedure thread_zombie(td:p_kthread);
|
|
begin
|
|
rw_wlock(zombie_lock);
|
|
TAILQ_INSERT_HEAD(@zombie_threads,td,@td^.td_zombie);
|
|
rw_wunlock(zombie_lock);
|
|
end;
|
|
|
|
{
|
|
* Reap zombie resources.
|
|
}
|
|
procedure thread_reap();
|
|
var
|
|
td_first,td_next:p_kthread;
|
|
begin
|
|
if rw_try_wlock(zombie_lock) then
|
|
begin
|
|
if (not TAILQ_EMPTY(@zombie_threads)) then
|
|
begin
|
|
td_first:=TAILQ_FIRST(@zombie_threads);
|
|
|
|
while (td_first<>nil) do
|
|
begin
|
|
td_next:=TAILQ_NEXT(td_first,@td_first^.td_slpq);
|
|
|
|
if cpu_thread_finished(td_first) then
|
|
begin
|
|
//Writeln('thread_reap:',HexStr(td_first));
|
|
TAILQ_REMOVE(@zombie_threads,td_first,@td_first^.td_zombie);
|
|
thread_free(td_first);
|
|
end else
|
|
begin
|
|
Break;
|
|
end;
|
|
|
|
td_first:=td_next;
|
|
end;
|
|
end;
|
|
|
|
rw_wunlock(zombie_lock);
|
|
end;
|
|
end;
|
|
|
|
function thread_alloc(pages:Word):p_kthread;
|
|
begin
|
|
thread_reap();
|
|
|
|
Result:=cpu_thread_alloc(pages);
|
|
|
|
mtx_init(Result^.tdq_lock,'tdq_lock');
|
|
Result^.td_lock:=@Result^.tdq_lock;
|
|
|
|
Result^.td_state:=TDS_INACTIVE;
|
|
Result^.td_lend_user_pri:=PRI_MAX;
|
|
|
|
Result^.td_sleepqueue:=sleepq_alloc();
|
|
umtx_thread_init(Result);
|
|
end;
|
|
|
|
procedure thread_free(td:p_kthread);
|
|
begin
|
|
mtx_destroy(td^.tdq_lock);
|
|
sleepq_free(td^.td_sleepqueue);
|
|
rlqentry_free(td^.td_rlqe);
|
|
umtx_thread_fini(td);
|
|
cpu_thread_free(td);
|
|
end;
|
|
|
|
procedure thread_inc_ref(td:p_kthread); public;
|
|
begin
|
|
System.InterlockedIncrement(td^.td_ref);
|
|
end;
|
|
|
|
procedure thread_dec_ref(td:p_kthread); public;
|
|
begin
|
|
if (System.InterlockedDecrement(td^.td_ref)=0) then
|
|
begin
|
|
thread_free_local_buffer(td);
|
|
thread_zombie(td);
|
|
end;
|
|
end;
|
|
|
|
procedure thread_lock_assert(td:p_kthread); public;
|
|
begin
|
|
mtx_assert(td^.td_lock^);
|
|
end;
|
|
|
|
procedure thread_lock(td:p_kthread); public;
|
|
var
|
|
m:p_mtx;
|
|
begin
|
|
repeat
|
|
m:=td^.td_lock;
|
|
Assert(m<>nil,'thread_lock');
|
|
mtx_lock(m^);
|
|
if (m=td^.td_lock) then
|
|
begin
|
|
Break;
|
|
end;
|
|
mtx_unlock(m^);
|
|
until false;
|
|
end;
|
|
|
|
procedure thread_unlock(td:p_kthread); public;
|
|
begin
|
|
mtx_unlock(td^.td_lock^);
|
|
end;
|
|
|
|
procedure thread_link(td:p_kthread);
|
|
begin
|
|
td^.td_state:=TDS_INACTIVE;
|
|
td^.td_flags:=TDF_INMEM;
|
|
|
|
sigqueue_init(@td^.td_sigqueue);
|
|
|
|
System.InterlockedIncrement(p_numthreads);
|
|
end;
|
|
|
|
procedure thread_unlink(td:p_kthread);
|
|
begin
|
|
System.InterlockedDecrement(p_numthreads)
|
|
end;
|
|
|
|
function tdfind(tid:DWORD):p_kthread; public;
|
|
Var
|
|
data:PPointer;
|
|
begin
|
|
Result:=nil;
|
|
rw_rlock(tidhash_lock);
|
|
|
|
data:=HAMT_search32(@tidhashtbl,tid);
|
|
|
|
if (data<>nil) then
|
|
begin
|
|
Result:=data^;
|
|
end;
|
|
|
|
if (Result<>nil) then
|
|
begin
|
|
thread_inc_ref(Result);
|
|
end;
|
|
|
|
rw_runlock(tidhash_lock);
|
|
end;
|
|
|
|
procedure tidhash_add(td:p_kthread);
|
|
var
|
|
data:PPointer;
|
|
begin
|
|
rw_wlock(tidhash_lock);
|
|
|
|
TAILQ_INSERT_HEAD(@p_threads, td, @td^.td_plist);
|
|
|
|
data:=HAMT_insert32(@tidhashtbl,td^.td_tid,td);
|
|
|
|
if (data<>nil) then
|
|
begin
|
|
if (data^=td) then
|
|
begin
|
|
thread_inc_ref(td);
|
|
end;
|
|
end;
|
|
|
|
rw_wunlock(tidhash_lock);
|
|
end;
|
|
|
|
procedure tidhash_remove(td:p_kthread);
|
|
var
|
|
data:Pointer;
|
|
begin
|
|
data:=nil;
|
|
rw_wlock(tidhash_lock);
|
|
|
|
TAILQ_REMOVE(@p_threads, td, @td^.td_plist);
|
|
|
|
HAMT_delete32(@tidhashtbl,td^.td_tid,@data);
|
|
|
|
rw_wunlock(tidhash_lock);
|
|
|
|
if (data=td) then
|
|
begin
|
|
thread_dec_ref(td);
|
|
end;
|
|
end;
|
|
|
|
procedure threads_lock; public;
|
|
begin
|
|
rw_wlock(tidhash_lock);
|
|
end;
|
|
|
|
function threads_trylock:Boolean; public;
|
|
begin
|
|
Result:=rw_try_wlock(tidhash_lock);
|
|
end;
|
|
|
|
procedure threads_unlock; public;
|
|
begin
|
|
rw_wunlock(tidhash_lock);
|
|
end;
|
|
|
|
|
|
procedure KernSetThreadDebugName(newtd:p_kthread;prefix:PChar);
|
|
var
|
|
//td:p_kthread;
|
|
//backup:array[0..1] of QWORD;
|
|
name:shortstring;
|
|
begin
|
|
name:=shortstring(prefix)+shortstring(newtd^.td_name)+':'+IntToStr(newtd^.td_tid);
|
|
|
|
cpu_thread_set_name(newtd,name);
|
|
|
|
{
|
|
td:=curkthread;
|
|
|
|
if (td<>nil) then
|
|
begin
|
|
//prevent bullshit in ntdll:__chkstk
|
|
backup[0]:=PQWORD(td^.td_kstack.stack)[-1];
|
|
backup[1]:=PQWORD(td^.td_kstack.stack)[-2];
|
|
PQWORD(td^.td_kstack.stack)[-1]:=0;
|
|
PQWORD(td^.td_kstack.stack)[-2]:=0;
|
|
end;
|
|
|
|
SetThreadDebugName(newtd^.td_tid,name);
|
|
|
|
if (td<>nil) then
|
|
begin
|
|
//restore stack
|
|
PQWORD(td^.td_kstack.stack)[-1]:=backup[0];
|
|
PQWORD(td^.td_kstack.stack)[-2]:=backup[1];
|
|
end;
|
|
}
|
|
end;
|
|
|
|
procedure before_start(td:p_kthread);
|
|
begin
|
|
td:=curkthread;
|
|
|
|
InitThread(td^.td_ustack.stack-td^.td_ustack.sttop);
|
|
|
|
Set8087CW(__INITIAL_FPUCW__);
|
|
SetMXCSR (__INITIAL_MXCSR__);
|
|
|
|
if (init_tty_cb<>nil) then
|
|
begin
|
|
init_tty_cb();
|
|
end;
|
|
|
|
//switch
|
|
set_pcb_flags(curkthread,PCB_IS_JIT); //force JIT mode
|
|
ipi_sigreturn;
|
|
Writeln(stderr,'I''m a teapot!');
|
|
end;
|
|
|
|
procedure before_start_kern(td:p_kthread);
|
|
type
|
|
t_cb=procedure(arg:QWORD);
|
|
begin
|
|
td:=curkthread;
|
|
|
|
InitThread(td^.td_ustack.stack-td^.td_ustack.sttop);
|
|
|
|
Set8087CW(__INITIAL_FPUCW__);
|
|
SetMXCSR (__INITIAL_MXCSR__);
|
|
|
|
if (init_tty_cb<>nil) then
|
|
begin
|
|
init_tty_cb();
|
|
end;
|
|
|
|
//call
|
|
t_cb(td^.td_frame.tf_rip)(td^.td_frame.tf_rdi);
|
|
|
|
kthread_exit();
|
|
end;
|
|
|
|
|
|
procedure thread0_param(td:p_kthread);
|
|
begin
|
|
td^.td_base_user_pri:=700;
|
|
td^.td_lend_user_pri:=1023;
|
|
td^.td_base_pri :=68;
|
|
td^.td_priority :=68;
|
|
td^.td_pri_class :=10;
|
|
td^.td_user_pri :=700;
|
|
end;
|
|
|
|
function create_thread(td :p_kthread; //calling thread
|
|
ctx :p_mcontext_t;
|
|
start_func:Pointer;
|
|
arg :Pointer;
|
|
stack_base:Pointer;
|
|
stack_size:QWORD;
|
|
tls_base :Pointer;
|
|
child_tid :PDWORD;
|
|
parent_tid:PDWORD;
|
|
rtp :p_rtprio;
|
|
name :PChar
|
|
):Integer;
|
|
var
|
|
newtd:p_kthread;
|
|
stack:stack_t;
|
|
|
|
wrap:Pointer;
|
|
|
|
n:Integer;
|
|
begin
|
|
Result:=0;
|
|
if (p_numthreads>=max_threads_per_proc) then
|
|
begin
|
|
Exit(EPROCLIM);
|
|
end;
|
|
|
|
writeln('create_thread[',name,']'#13#10,
|
|
' start_func:0x',HexStr(start_func),#13#10,
|
|
' arg :0x',HexStr(arg),#13#10,
|
|
' stack_base:0x',HexStr(stack_base),#13#10,
|
|
' stack_size:0x',HexStr(stack_size,16),#13#10,
|
|
' tls_base :0x',HexStr(tls_base)
|
|
);
|
|
|
|
thread_reap();
|
|
|
|
if (rtp<>nil) then
|
|
begin
|
|
Case (rtp^._type and $fff7) of //RTP_PRIO_BASE
|
|
RTP_PRIO_IDLE:
|
|
begin
|
|
if (rtp^._prio<>960) then Exit(EINVAL);
|
|
end;
|
|
RTP_PRIO_NORMAL:
|
|
begin
|
|
if (rtp^._prio>959) then Exit(EINVAL);
|
|
end;
|
|
PRI_REALTIME:
|
|
begin
|
|
if (rtp^._prio>767) then Exit(EINVAL);
|
|
end;
|
|
else
|
|
Exit(EINVAL)
|
|
end;
|
|
end;
|
|
|
|
if (stack_size<$1000) then
|
|
begin
|
|
if (ctx<>nil) then
|
|
begin
|
|
stack_size:=$1000;
|
|
end else
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
end;
|
|
|
|
if (ptruint(stack_base)<$1000) then
|
|
begin
|
|
if (ctx<>nil) then
|
|
begin
|
|
stack_base:=Pointer(ctx^.mc_rsp-stack_size);
|
|
|
|
//re check
|
|
if (ptruint(stack_base)<$1000) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
end else
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
end;
|
|
|
|
newtd:=thread_alloc(0);
|
|
if (newtd=nil) then Exit(ENOMEM);
|
|
|
|
thread0_param(newtd);
|
|
|
|
//user stack
|
|
newtd^.td_ustack.stack:=stack_base+stack_size;
|
|
newtd^.td_ustack.sttop:=stack_base;
|
|
//user stack
|
|
|
|
|
|
//seh wrapper
|
|
wrap:=@before_start;
|
|
seh_wrapper_before(newtd,wrap);
|
|
//seh wrapper
|
|
|
|
stack.ss_sp :=newtd^.td_kstack.sttop;
|
|
stack.ss_size:=(ptruint(newtd^.td_kstack.stack)-ptruint(newtd^.td_kstack.sttop));
|
|
|
|
n:=cpu_thread_create(newtd,
|
|
stack.ss_sp,
|
|
stack.ss_size,
|
|
wrap,
|
|
Pointer(newtd));
|
|
|
|
if (n<>0) then
|
|
begin
|
|
thread_free(newtd);
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
writeln('create_thread[',name,']'#13#10,
|
|
' newtd:0x',HexStr(newtd),#13#10,
|
|
' tid:',newtd^.td_tid
|
|
);
|
|
|
|
if (child_tid<>nil) then
|
|
begin
|
|
n:=suword32(child_tid^,newtd^.td_tid);
|
|
if (n<>0) then
|
|
begin
|
|
cpu_thread_terminate(newtd);
|
|
thread_free(newtd);
|
|
Exit(EFAULT);
|
|
end;
|
|
end;
|
|
|
|
if (parent_tid<>nil) then
|
|
begin
|
|
n:=suword32(parent_tid^,newtd^.td_tid);
|
|
if (n<>0) then
|
|
begin
|
|
cpu_thread_terminate(newtd);
|
|
thread_free(newtd);
|
|
Exit(EFAULT);
|
|
end;
|
|
end;
|
|
|
|
if (ctx<>nil) then
|
|
begin
|
|
// old way to set user context
|
|
n:=set_mcontext(newtd,ctx);
|
|
if (n<>0) then
|
|
begin
|
|
cpu_thread_terminate(newtd);
|
|
thread_free(newtd);
|
|
Exit(n);
|
|
end;
|
|
end else
|
|
begin
|
|
// Set up our machine context.
|
|
stack.ss_sp :=stack_base;
|
|
stack.ss_size:=stack_size;
|
|
// Set upcall address to user thread entry function.
|
|
cpu_set_upcall_kse(newtd,start_func,arg,@stack);
|
|
// Setup user TLS address and TLS pointer register.
|
|
cpu_set_fsbase(newtd,tls_base);
|
|
Writeln('set_fsbase=0x',HexStr(tls_base));
|
|
//init FPU
|
|
fpuinit(newtd);
|
|
end;
|
|
|
|
//seh wrapper
|
|
wrap:=@before_start;
|
|
seh_wrapper_after(newtd,wrap);
|
|
//seh wrapper
|
|
|
|
if (td<>nil) then
|
|
begin
|
|
newtd^.td_sigmask:=td^.td_sigmask;
|
|
end;
|
|
|
|
thread_link(newtd);
|
|
|
|
if (name<>nil) then
|
|
begin
|
|
Move(name^,newtd^.td_name,SizeOf(t_td_name));
|
|
end;
|
|
KernSetThreadDebugName(newtd,'ps4:');
|
|
|
|
sched_fork_thread(td,newtd);
|
|
|
|
tidhash_add(newtd);
|
|
|
|
if (rtp<>nil) then
|
|
begin
|
|
if (td=nil) then
|
|
begin
|
|
rtp_to_pri(rtp,newtd);
|
|
sched_prio(newtd,newtd^.td_user_pri);
|
|
end else
|
|
if (td^.td_pri_class<>PRI_TIMESHARE) then
|
|
begin
|
|
rtp_to_pri(rtp,newtd);
|
|
sched_prio(newtd,newtd^.td_user_pri);
|
|
end;
|
|
end else
|
|
begin
|
|
sched_prio(newtd,newtd^.td_user_pri);
|
|
end;
|
|
|
|
n:=cpu_sched_add(newtd);
|
|
if (n<>0) then
|
|
begin
|
|
cpu_thread_terminate(newtd);
|
|
thread_free(newtd);
|
|
Exit(EFAULT);
|
|
end;
|
|
end;
|
|
|
|
function kthread_add(func,arg:Pointer;newtdp:pp_kthread;pages:Word;name:PChar):Integer; public;
|
|
var
|
|
td:p_kthread;
|
|
newtd:p_kthread;
|
|
stack:stack_t;
|
|
|
|
wrap:Pointer;
|
|
|
|
n:Integer;
|
|
begin
|
|
Result:=0;
|
|
|
|
threadinit;
|
|
|
|
if (func=nil) or
|
|
(newtdp=nil) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
td:=curkthread;
|
|
|
|
if (td<>nil) then
|
|
begin
|
|
thread_reap();
|
|
end;
|
|
|
|
newtd:=thread_alloc(pages);
|
|
if (newtd=nil) then Exit(ENOMEM);
|
|
|
|
thread0_param(newtd);
|
|
|
|
stack.ss_sp :=newtd^.td_kstack.sttop;
|
|
stack.ss_size:=(ptruint(newtd^.td_kstack.stack)-ptruint(newtd^.td_kstack.sttop));
|
|
|
|
//user stack
|
|
newtd^.td_ustack.stack:=newtd^.td_kstack.stack;
|
|
newtd^.td_ustack.sttop:=newtd^.td_kstack.sttop;
|
|
//user stack
|
|
|
|
//seh wrapper
|
|
wrap:=@before_start_kern;
|
|
seh_wrapper_before(newtd,wrap);
|
|
//seh wrapper
|
|
|
|
n:=cpu_thread_create(newtd,
|
|
stack.ss_sp,
|
|
stack.ss_size,
|
|
wrap,
|
|
Pointer(newtd));
|
|
|
|
if (n<>0) then
|
|
begin
|
|
Writeln(StdErr,'failed cpu_thread_create:0x',HexStr(n,8));
|
|
thread_free(newtd);
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
cpu_set_upcall_kse(newtd,func,arg,@stack);
|
|
|
|
//init FPU
|
|
fpuinit(newtd);
|
|
|
|
//seh wrapper
|
|
wrap:=@before_start_kern;
|
|
seh_wrapper_after(newtd,wrap);
|
|
//seh wrapper
|
|
|
|
if (td<>nil) then
|
|
begin
|
|
newtd^.td_sigmask:=td^.td_sigmask;
|
|
end;
|
|
|
|
thread_link(newtd);
|
|
|
|
newtd^.td_pflags:=newtd^.td_pflags or TDP_KTHREAD;
|
|
|
|
if (name<>nil) then
|
|
begin
|
|
Move(name^,newtd^.td_name,SizeOf(t_td_name));
|
|
end;
|
|
KernSetThreadDebugName(newtd,'kern:');
|
|
|
|
sched_fork_thread(td,newtd);
|
|
|
|
tidhash_add(newtd);
|
|
|
|
thread_inc_ref(newtd);
|
|
|
|
n:=cpu_sched_add(newtd);
|
|
if (n<>0) then
|
|
begin
|
|
cpu_thread_terminate(newtd);
|
|
thread_dec_ref(newtd);
|
|
thread_free(newtd);
|
|
Exit(EFAULT);
|
|
end;
|
|
|
|
newtdp^:=newtd;
|
|
end;
|
|
|
|
function kern_thr_new(td:p_kthread;param:p_thr_param):Integer;
|
|
var
|
|
rtp:t_rtprio;
|
|
rtpp:p_rtprio;
|
|
name:t_td_name;
|
|
begin
|
|
Result:=0;
|
|
rtpp:=nil;
|
|
|
|
if (param^.rtp<>nil) then
|
|
begin
|
|
Result:=copyin(param^.rtp,@rtp,Sizeof(t_rtprio));
|
|
if (Result<>0) then Exit;
|
|
rtpp:=@rtp;
|
|
end;
|
|
|
|
name:=Default(t_td_name);
|
|
|
|
if (param^.name<>nil) then
|
|
begin
|
|
Result:=copyinstr(param^.name,@name,32,nil);
|
|
if (Result<>0) then Exit;
|
|
end;
|
|
|
|
Result:=create_thread(td,
|
|
nil,
|
|
param^.start_func,
|
|
param^.arg,
|
|
param^.stack_base,
|
|
param^.stack_size,
|
|
param^.tls_base,
|
|
param^.child_tid,
|
|
param^.parent_tid,
|
|
rtpp,
|
|
@name);
|
|
end;
|
|
|
|
function sys_thr_new(_param:Pointer;_size:Integer):Integer;
|
|
var
|
|
param:thr_param;
|
|
begin
|
|
if (_size<0) or (_size>Sizeof(thr_param)) then Exit(EINVAL);
|
|
|
|
param:=Default(thr_param);
|
|
|
|
Result:=copyin(_param,@param,_size);
|
|
if (Result<>0) then Exit;
|
|
|
|
Result:=kern_thr_new(curkthread,@param);
|
|
end;
|
|
|
|
function sys_thr_create(ctx:Pointer;id:PDWORD;flags:Integer):Integer;
|
|
var
|
|
_ctx:ucontext_t;
|
|
begin
|
|
Result:=copyin(ctx,@_ctx,sizeof(ucontext_t));
|
|
if (Result<>0) then Exit;
|
|
|
|
//flags ignored
|
|
Result:=create_thread(curkthread,
|
|
@_ctx.uc_mcontext,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
0,
|
|
nil,
|
|
id,
|
|
nil,
|
|
nil,
|
|
nil);
|
|
|
|
end;
|
|
|
|
procedure thread_exit;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
td:=curkthread;
|
|
if (td=nil) then Exit;
|
|
|
|
ASSERT(TAILQ_EMPTY(@td^.td_sigqueue.sq_list),'signal pending');
|
|
|
|
td^.td_state:=TDS_INACTIVE;
|
|
|
|
tidhash_remove(td);
|
|
thread_unlink(td);
|
|
|
|
umtx_thread_exit(td);
|
|
|
|
jit_ctx_free(td);
|
|
|
|
//free
|
|
thread_dec_ref(td);
|
|
|
|
DoneThread;
|
|
|
|
cpu_sched_throw;
|
|
end;
|
|
|
|
function sys_thr_self(id:PDWORD):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
if (id=nil) then Exit(EINVAL);
|
|
|
|
td:=curkthread;
|
|
if (td=nil) then Exit(EFAULT);
|
|
|
|
Result:=suword32(id^,td^.td_tid);
|
|
if (Result<>0) then Exit(EFAULT);
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure sys_thr_exit(state:PDWORD);
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
td:=curkthread;
|
|
if (td=nil) then Exit;
|
|
|
|
if (state<>nil) then
|
|
begin
|
|
//join wakeup
|
|
suword32(state^, 1);
|
|
kern_umtx_wake(td,Pointer(state),High(Integer),0);
|
|
end;
|
|
|
|
tdsigcleanup(td);
|
|
//thread_stopped(p);
|
|
thread_reap();
|
|
thread_exit();
|
|
// NOTREACHED
|
|
end;
|
|
|
|
procedure kthread_exit(); public;
|
|
begin
|
|
thread_reap();
|
|
thread_exit();
|
|
// NOTREACHED
|
|
end;
|
|
|
|
procedure thread_suspend_all(exclude:p_kthread); public;
|
|
var
|
|
td,ttd:p_kthread;
|
|
begin
|
|
td:=curkthread;
|
|
|
|
threads_lock;
|
|
|
|
ttd:=TAILQ_FIRST(@p_threads);
|
|
while (ttd<>nil) do
|
|
begin
|
|
|
|
if (ttd<>td) and (ttd<>exclude) then
|
|
begin
|
|
md_suspend(ttd);
|
|
end;
|
|
|
|
ttd:=TAILQ_NEXT(ttd,@ttd^.td_plist)
|
|
end;
|
|
|
|
threads_unlock;
|
|
end;
|
|
|
|
procedure thread_resume_all(exclude:p_kthread); public;
|
|
var
|
|
td,ttd:p_kthread;
|
|
begin
|
|
td:=curkthread;
|
|
|
|
threads_lock;
|
|
|
|
ttd:=TAILQ_FIRST(@p_threads);
|
|
while (ttd<>nil) do
|
|
begin
|
|
|
|
if (ttd<>td) and (ttd<>exclude) then
|
|
begin
|
|
md_resume(ttd);
|
|
end;
|
|
|
|
ttd:=TAILQ_NEXT(ttd,@ttd^.td_plist)
|
|
end;
|
|
|
|
threads_unlock;
|
|
end;
|
|
|
|
function sys_thr_kill(id,sig:Integer):Integer;
|
|
var
|
|
td,ttd:p_kthread;
|
|
ksi:ksiginfo_t;
|
|
begin
|
|
td:=curkthread;
|
|
|
|
thread_reap();
|
|
|
|
ksiginfo_init(@ksi);
|
|
ksi.ksi_info.si_signo:=sig;
|
|
ksi.ksi_info.si_code :=SI_LWP;
|
|
ksi.ksi_info.si_pid :=p_proc.p_pid;
|
|
|
|
if (id=-1) then //all
|
|
begin
|
|
if (sig<>0) and (not _SIG_VALID(sig)) then
|
|
begin
|
|
Result:=EINVAL;
|
|
end else
|
|
begin
|
|
Result:=ESRCH;
|
|
PROC_LOCK;
|
|
|
|
threads_lock;
|
|
|
|
ttd:=TAILQ_FIRST(@p_threads);
|
|
while (ttd<>nil) do
|
|
begin
|
|
|
|
if (ttd<>td) then
|
|
begin
|
|
Result:=0;
|
|
if (sig=0) then Break;
|
|
tdksignal(ttd,sig,@ksi);
|
|
end;
|
|
|
|
|
|
ttd:=TAILQ_NEXT(ttd,@ttd^.td_plist)
|
|
end;
|
|
|
|
threads_unlock;
|
|
|
|
PROC_UNLOCK;
|
|
end;
|
|
end else
|
|
begin
|
|
Result:=0;
|
|
|
|
td:=tdfind(DWORD(id));
|
|
if (td=nil) then Exit(ESRCH);
|
|
|
|
if (sig=0) then
|
|
begin
|
|
//
|
|
end else
|
|
if (not _SIG_VALID(sig)) then
|
|
Result:=EINVAL
|
|
else
|
|
begin
|
|
PROC_LOCK;
|
|
tdksignal(td,sig,@ksi);
|
|
PROC_UNLOCK;
|
|
end;
|
|
|
|
thread_dec_ref(td);
|
|
end;
|
|
end;
|
|
|
|
function sys_thr_kill2(pid,id,sig:Integer):Integer;
|
|
begin
|
|
if (pid<>0) and (pid<>p_proc.p_pid) then
|
|
begin
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
Result:=sys_thr_kill(id,sig);
|
|
end;
|
|
|
|
function kern_thr_suspend(td:p_kthread;tsp:p_timespec):Integer;
|
|
var
|
|
tv:Int64;
|
|
begin
|
|
Result:=0;
|
|
|
|
thread_reap();
|
|
|
|
if ((td^.td_pflags and TDP_WAKEUP)<>0) then
|
|
begin
|
|
td^.td_pflags:=td^.td_pflags and (not TDP_WAKEUP);
|
|
Exit(0);
|
|
end;
|
|
|
|
tv:=0;
|
|
if (tsp<>nil) then
|
|
begin
|
|
if (tsp^.tv_sec=0) and (tsp^.tv_nsec=0) then
|
|
begin
|
|
Result:=EWOULDBLOCK;
|
|
end else
|
|
begin
|
|
tv:=TIMESPEC_TO_UNIT(tsp);
|
|
tv:=tvtohz(tv);
|
|
end;
|
|
end;
|
|
|
|
PROC_LOCK;
|
|
|
|
if (Result=0) and ((td^.td_flags and TDF_THRWAKEUP)=0) then
|
|
begin
|
|
Result:=msleep(td,@p_proc.p_mtx,PCATCH,'lthr',tv);
|
|
end;
|
|
|
|
if ((td^.td_flags and TDF_THRWAKEUP)<>0) then
|
|
begin
|
|
thread_lock(td);
|
|
td^.td_flags:=td^.td_flags and (not TDF_THRWAKEUP);
|
|
thread_unlock(td);
|
|
PROC_UNLOCK;
|
|
Exit(0);
|
|
end;
|
|
|
|
PROC_UNLOCK;
|
|
|
|
if (Result=EWOULDBLOCK) then
|
|
begin
|
|
Result:=ETIMEDOUT;
|
|
end else
|
|
if (Result=ERESTART) then
|
|
begin
|
|
if (tv<>0) then
|
|
begin
|
|
Result:=EINTR;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function sys_thr_suspend(timeout:Pointer):Integer;
|
|
var
|
|
td:p_kthread;
|
|
ts:timespec;
|
|
tsp:p_timespec;
|
|
begin
|
|
td:=curkthread;
|
|
if (td=nil) then Exit(-1);
|
|
|
|
tsp:=nil;
|
|
if (timeout<>nil) then
|
|
begin
|
|
Result:=umtx_copyin_timeout(timeout,@ts);
|
|
if (Result<>0) then Exit;
|
|
tsp:=@ts;
|
|
end;
|
|
|
|
Result:=kern_thr_suspend(td,tsp);
|
|
end;
|
|
|
|
function sys_thr_wake(id:DWORD):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
Result:=0;
|
|
td:=curkthread;
|
|
|
|
thread_reap();
|
|
|
|
if (td<>nil) then
|
|
if (id=td^.td_tid) then
|
|
begin
|
|
td^.td_pflags:=td^.td_pflags or TDP_WAKEUP;
|
|
Exit(0);
|
|
end;
|
|
|
|
td:=tdfind(DWORD(id));
|
|
if (td=nil) then Exit(ESRCH);
|
|
|
|
thread_lock(td);
|
|
td^.td_flags:=td^.td_flags or TDF_THRWAKEUP;
|
|
thread_unlock(td);
|
|
|
|
wakeup(td); //broadcast
|
|
|
|
thread_dec_ref(td);
|
|
end;
|
|
|
|
function sys_thr_set_name(id:DWORD;pname:PChar):Integer;
|
|
var
|
|
td:p_kthread;
|
|
name:t_td_name;
|
|
begin
|
|
Result:=0;
|
|
|
|
thread_reap();
|
|
|
|
name:=Default(t_td_name);
|
|
if (pname<>nil) then
|
|
begin
|
|
Result:=copyinstr(pname,@name,32,nil);
|
|
if (Result<>0) then Exit;
|
|
end;
|
|
|
|
if (Integer(id)=-1) then
|
|
begin
|
|
kern_proc.p_proc.p_comm:=name;
|
|
Exit;
|
|
end;
|
|
|
|
td:=tdfind(DWORD(id));
|
|
if (td=nil) then Exit(ESRCH);
|
|
|
|
thread_lock(td);
|
|
|
|
td^.td_name:=name;
|
|
KernSetThreadDebugName(td,'ps4:');
|
|
|
|
thread_unlock(td);
|
|
thread_dec_ref(td);
|
|
end;
|
|
|
|
function strnlen(s:PChar;maxlen:ptrint):ptrint;
|
|
var
|
|
len:size_t;
|
|
begin
|
|
For len:=0 to maxlen-1 do
|
|
begin
|
|
if (s^=#0) then Break;
|
|
Inc(s);
|
|
end;
|
|
Exit(len);
|
|
end;
|
|
|
|
function sys_thr_get_name(id:DWORD;pname:PChar):Integer;
|
|
var
|
|
td:p_kthread;
|
|
name:t_td_name;
|
|
len:ptrint;
|
|
begin
|
|
Result:=0;
|
|
|
|
thread_reap();
|
|
|
|
td:=tdfind(DWORD(id));
|
|
if (td=nil) then
|
|
begin
|
|
//sceSblACMgrIsSystemUcred
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
thread_lock(td);
|
|
|
|
name:=td^.td_name;
|
|
|
|
thread_unlock(td);
|
|
thread_dec_ref(td);
|
|
|
|
len:=strnlen(name,31);
|
|
Result:=copyout(@name,pname,len+1);
|
|
end;
|
|
|
|
function sys_amd64_set_fsbase(base:Pointer):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
Result:=0;
|
|
td:=curkthread;
|
|
if (td=nil) then Exit(-1);
|
|
cpu_set_fsbase(td,base);
|
|
end;
|
|
|
|
function sys_amd64_get_fsbase(base:PPointer):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
Result:=0;
|
|
td:=curkthread;
|
|
if (td=nil) or (base=nil) then Exit(-1);
|
|
base^:=td^.pcb_fsbase;
|
|
end;
|
|
|
|
function sys_amd64_set_gsbase(base:Pointer):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
Result:=0;
|
|
td:=curkthread;
|
|
if (td=nil) then Exit(-1);
|
|
cpu_set_gsbase(td,base);
|
|
end;
|
|
|
|
function sys_amd64_get_gsbase(base:PPointer):Integer;
|
|
var
|
|
td:p_kthread;
|
|
begin
|
|
Result:=0;
|
|
td:=curkthread;
|
|
if (td=nil) or (base=nil) then Exit(-1);
|
|
base^:=td^.pcb_gsbase;
|
|
end;
|
|
|
|
end.
|
|
|