mirror of https://github.com/flysand7/ciabatta.git
Thread cancellation
This commit is contained in:
parent
8c5c5adeca
commit
27ee227f8e
|
@ -4,6 +4,7 @@ bits 64
|
||||||
section .text
|
section .text
|
||||||
global _rt_thread_start
|
global _rt_thread_start
|
||||||
|
|
||||||
|
extern _rt_thread_setup
|
||||||
extern _rt_thread_finish
|
extern _rt_thread_finish
|
||||||
|
|
||||||
; flags, &stack[-2], &parent_tid, &child_tid, 0
|
; flags, &stack[-2], &parent_tid, &child_tid, 0
|
||||||
|
@ -42,11 +43,11 @@ _rt_thread_start:
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jnz .exit
|
jnz .exit
|
||||||
; If child, jump to thread function
|
; If child, jump to thread function
|
||||||
pop rax ; thread_fn
|
pop rdi ; thread_fn
|
||||||
pop rdi ; ctx
|
pop rsi ; ctx
|
||||||
; Align the stack
|
; Align the stack
|
||||||
and rsp, -16
|
and rsp, -16
|
||||||
call rax
|
call _rt_thread_setup
|
||||||
; Make return value the first arg and call thread finish routine
|
; Make return value the first arg and call thread finish routine
|
||||||
mov rdi, rax
|
mov rdi, rax
|
||||||
call _rt_thread_finish
|
call _rt_thread_finish
|
||||||
|
|
|
@ -11,27 +11,36 @@ struct _LD_CRT_Params {
|
||||||
void *tls_image_base;
|
void *tls_image_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
// These control how the thread should behave once it's terminated
|
// These signal a decision made by the main thread
|
||||||
// if the main thread didn't call join nor detach, the thread
|
// about whether a thread should be detached or joined
|
||||||
// would just wait until one of them is set.
|
// If its not set, upon finishing the thread will wait
|
||||||
// If the thread is joined,
|
// until one of these flags is set.
|
||||||
// would signal main thread that it has finished and exit without
|
// If the flag was set to detached, the thread will perform
|
||||||
// cleaning up the resources (thrd_join does that for us)
|
// cleanup on its own after it reached the end of execution.
|
||||||
// If the thread is detached, it would clear it's resources and exit
|
// If the flag was set to joined, it cannot perform the
|
||||||
#define _LD_THREAD_BEHAVIOUR_NOT_SET 0x0
|
// cleanup on its own. For if it did, the check on the state_finish
|
||||||
#define _LD_THREAD_BEHAVIOUR_JOIN 0x1
|
// futex might fail due to TCB being destroyed by the thread.
|
||||||
#define _LD_THREAD_BEHAVIOUR_DETACH 0x2
|
// Which is why we first need to make sure the thread stopped working
|
||||||
#define _LD_THREAD_BEHAVIOUR_FINISH 0x3
|
// and we don't need any of its TCB fields before cleaning it up on
|
||||||
|
// the main thread.
|
||||||
|
#define _LD_THREAD_STATE_NOT_YET 0x0
|
||||||
|
#define _LD_THREAD_STATE_DETACHED 0x1
|
||||||
|
#define _LD_THREAD_STATE_JOINED 0x2
|
||||||
|
|
||||||
struct _LD_Thread_Block typedef _LD_Thread_Block;
|
struct _LD_Thread_Block typedef _LD_Thread_Block;
|
||||||
struct _LD_Thread_Block {
|
struct _LD_Thread_Block {
|
||||||
/* +0x00 */ u64 thread_id;
|
/* DO NOT REORDER OR CHANGE SIZES (these comments are supposed to make it hard) */
|
||||||
/* +0x08 */ u64 parent_id;
|
/* +0x00 */ i32 thread_id;
|
||||||
/* +0x10 */ _Atomic(u32) thread_behaviour; /* One of the CIA_THREAD_BEHAVIOR_* constants */
|
/* +0x04 */ i32 state_finish;
|
||||||
/* +0x14 */ _Atomic(i32) thread_finished;
|
/* +0x08 */ _Atomic(u32) state_detach;
|
||||||
/* +0x18 */ u32 pad0;
|
/* +0x0c */ u32 exit_code;
|
||||||
/* +0x1c */ _Atomic(i32) exit_code;
|
/* +0x10 */ u64 pad0;
|
||||||
/* +0x20 */ u64 pad1;
|
/* +0x18 */ u64 pad1;
|
||||||
|
/* +0x20 */ u64 pad2;
|
||||||
/* +0x28 */ u64 stack_canary;
|
/* +0x28 */ u64 stack_canary;
|
||||||
|
/* Not ABI dependent as far as I care (for now) */
|
||||||
|
_LD_Thread_Block *next_tcb;
|
||||||
|
_LD_Thread_Block *prev_tcb;
|
||||||
|
_Atomic(u32) is_cancelled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define SIGHUP 1
|
||||||
|
#define SIGINT 2
|
||||||
|
#define SIGQUIT 3
|
||||||
|
#define SIGILL 4
|
||||||
|
#define SIGTRAP 5
|
||||||
|
#define SIGABRT 6
|
||||||
|
#define SIGBUS 7
|
||||||
|
#define SIGFPE 8
|
||||||
|
#define SIGKILL 9
|
||||||
|
#define SIGUSR1 10
|
||||||
|
#define SIGSEGV 11
|
||||||
|
#define SIGUSR2 12
|
||||||
|
#define SIGPIPE 13
|
||||||
|
#define SIGALRM 14
|
||||||
|
#define SIGTERM 15
|
||||||
|
#define SIGSTKFLT 16
|
||||||
|
#define SIGCHLD 17
|
||||||
|
#define SIGCONT 18
|
||||||
|
#define SIGSTOP 19
|
||||||
|
#define SIGTSTP 20
|
||||||
|
#define SIGTTIN 21
|
||||||
|
#define SIGTTOU 22
|
||||||
|
#define SIGURG 23
|
||||||
|
#define SIGXCPU 24
|
||||||
|
#define SIGXFSZ 25
|
||||||
|
#define SIGVTALRM 26
|
||||||
|
#define SIGPROF 27
|
||||||
|
#define SIGWINCH 28
|
||||||
|
#define SIGPOLL 29
|
||||||
|
#define SIGPWR 30
|
||||||
|
#define SIGSYS 31
|
||||||
|
#define SIGCLD SIGCHLD
|
||||||
|
#define SIGIO SIGPOLL
|
||||||
|
#define SIGIOT SIGABRT
|
||||||
|
|
||||||
|
// Custom guy
|
||||||
|
#define SIGCANCEL 33
|
||||||
|
|
||||||
|
#define SA_NOCLDSTOP 1
|
||||||
|
#define SA_NOCLDWAIT 2
|
||||||
|
#define SA_SIGINFO 4
|
||||||
|
#define SA_ONSTACK 0x08000000
|
||||||
|
#define SA_RESTART 0x10000000
|
||||||
|
#define SA_INTERRUPT 0x20000000
|
||||||
|
#define SA_NODEFER 0x40000000
|
||||||
|
#define SA_RESETHAND 0x80000000
|
||||||
|
#define SA_NOMASK SA_NODEFER
|
||||||
|
#define SA_ONESHOT SA_RESETHAND
|
||||||
|
#define SA_STACK SA_ONSTACK
|
||||||
|
|
||||||
|
#define SIG_BLOCK 0
|
||||||
|
#define SIG_SETMASK 2
|
||||||
|
#define SIG_UNBLOCK 1
|
||||||
|
|
||||||
|
union sigval {
|
||||||
|
i32 sival_int;
|
||||||
|
void *sival_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct siginfo_t typedef siginfo_t;
|
||||||
|
struct siginfo_t {
|
||||||
|
i32 si_signo;
|
||||||
|
i32 si_errno;
|
||||||
|
i32 si_code;
|
||||||
|
i32 si_trapno;
|
||||||
|
u32 si_pid;
|
||||||
|
u32 si_uid;
|
||||||
|
i32 si_status;
|
||||||
|
u64 si_utime;
|
||||||
|
u64 si_stime;
|
||||||
|
union sigval si_value;
|
||||||
|
i32 si_int;
|
||||||
|
void *si_ptr;
|
||||||
|
i32 si_overrun;
|
||||||
|
i32 si_timerid;
|
||||||
|
void *si_addr;
|
||||||
|
long si_band;
|
||||||
|
i32 si_fd;
|
||||||
|
short si_addr_lsb;
|
||||||
|
void *si_lower;
|
||||||
|
void *si_upper;
|
||||||
|
i32 si_pkey;
|
||||||
|
void *si_call_addr;
|
||||||
|
i32 si_syscall;
|
||||||
|
u32 si_arch;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct sigaction {
|
||||||
|
union {
|
||||||
|
void (*sa_handler)(i32);
|
||||||
|
void (*sa_sigaction)(i32, siginfo_t *, void *);
|
||||||
|
};
|
||||||
|
u64 sa_mask;
|
||||||
|
i32 sa_flags;
|
||||||
|
void (*sa_restorer)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline i32 sys_sigaction(i32 signum, const struct sigaction *restrict act, struct sigaction *restrict oldact) {
|
||||||
|
return (i32)syscall(SYS_rt_sigaction, signum, act, oldact, 1);
|
||||||
|
}
|
||||||
|
|
|
@ -417,6 +417,19 @@ static inline i64 sys_arch_prctl(int code, u64 value) {
|
||||||
return syscall(SYS_arch_prctl, code, value);
|
return syscall(SYS_arch_prctl, code, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline i64 sys_gettid() {
|
static inline u32 sys_gettid() {
|
||||||
return syscall(SYS_gettid);
|
return syscall(SYS_gettid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 sys_getpid() {
|
||||||
|
return syscall(SYS_getpid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline i32 sys_tkill(u32 tid, i32 sig) {
|
||||||
|
return syscall(SYS_tkill, tid, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline i32 sys_tgkill(u32 tgid, u32 tid, i32 sig) {
|
||||||
|
return syscall(SYS_tgkill, tgid, tid, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,6 @@ enum {
|
||||||
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
|
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
|
||||||
int thrd_join(thrd_t thr, int *out_exit_code);
|
int thrd_join(thrd_t thr, int *out_exit_code);
|
||||||
int thrd_detach(thrd_t thr);
|
int thrd_detach(thrd_t thr);
|
||||||
void thrd_yield();
|
void thrd_yield();
|
||||||
|
|
||||||
|
void thrd_terminate(thrd_t thr); /*TODO remove later*/
|
|
@ -62,6 +62,9 @@ static _RT_Status _rt_thread_yield();
|
||||||
static _RT_Status _rt_thread_sleep(u64 time);
|
static _RT_Status _rt_thread_sleep(u64 time);
|
||||||
static _RT_Status _rt_thread_get_timer_freq(u64 *freq);
|
static _RT_Status _rt_thread_get_timer_freq(u64 *freq);
|
||||||
|
|
||||||
|
// TODO: maybe replace with kill of single thread
|
||||||
|
static _RT_Status _rt_thread_cancell_all_running();
|
||||||
|
|
||||||
// Environment API
|
// Environment API
|
||||||
static _RT_Status _rt_shell_exec(char const *cmd);
|
static _RT_Status _rt_shell_exec(char const *cmd);
|
||||||
static _RT_Status _rt_env_get(char const *name);
|
static _RT_Status _rt_env_get(char const *name);
|
||||||
|
|
|
@ -9,12 +9,14 @@ static void *cia_tls_image_base;
|
||||||
#include <cia-ld/tcb.h>
|
#include <cia-ld/tcb.h>
|
||||||
|
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
#include <sys/mman.h>
|
#include <linux/sys/syscall.h>
|
||||||
#include <errno.h>
|
#include <linux/sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <linux/errno.h>
|
||||||
#include <sched.h>
|
#include <linux/signal.h>
|
||||||
#include "entry.c"
|
#include <linux/fcntl.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
#include <tinyrt.h>
|
#include <tinyrt.h>
|
||||||
#include "tinyrt.c"
|
#include "tinyrt.c"
|
||||||
#include "tinyrt-threads.c"
|
#include "tinyrt-threads.c"
|
||||||
|
#include "entry.c"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
#include <syscall.h>
|
|
||||||
|
|
||||||
static char stack_chk_fail_msg[] =
|
static char stack_chk_fail_msg[] =
|
||||||
"Stack check failed. "
|
"Stack check failed. "
|
||||||
"You've got a stack corruption somewhere. "
|
"You've got a stack corruption somewhere. "
|
||||||
|
@ -13,6 +11,7 @@ void __stack_chk_fail(void) {
|
||||||
|
|
||||||
extern int main(int argc, char **argv, char **envp);
|
extern int main(int argc, char **argv, char **envp);
|
||||||
static void _fileapi_init();
|
static void _fileapi_init();
|
||||||
|
static void _rt_threads_setup();
|
||||||
|
|
||||||
void _start(_LD_CRT_Params *params) {
|
void _start(_LD_CRT_Params *params) {
|
||||||
cia_stack_size = params->stack_size;
|
cia_stack_size = params->stack_size;
|
||||||
|
@ -22,9 +21,8 @@ void _start(_LD_CRT_Params *params) {
|
||||||
// char **envp = argv + (argc + 1);
|
// char **envp = argv + (argc + 1);
|
||||||
// init(argc, argv, envp);
|
// init(argc, argv, envp);
|
||||||
_fileapi_init();
|
_fileapi_init();
|
||||||
|
_rt_threads_setup();
|
||||||
int code = main(0, NULL, NULL);
|
int code = main(0, NULL, NULL);
|
||||||
// fini();
|
_rt_thread_cancell_all_running();
|
||||||
// glibc bug
|
|
||||||
// dl_fini();
|
|
||||||
sys_exit(code);
|
sys_exit(code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
static _LD_Thread_Block *thread_blocks_head = NULL;
|
||||||
|
|
||||||
extern i64 _rt_thread_start(
|
extern i64 _rt_thread_start(
|
||||||
u64 flags,
|
u64 flags,
|
||||||
void *stack_base,
|
void *stack_base,
|
||||||
|
@ -9,6 +11,25 @@ extern i64 _rt_thread_start(
|
||||||
void *ctx
|
void *ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static void thread_sigcancel_handler(int sig, siginfo_t *info, void *ucontext) {
|
||||||
|
_LD_Thread_Block *tcb = (void *)((u64)__builtin_frame_address(0) & ~(cia_stack_size - 1));
|
||||||
|
u32 is_cancelled = atomic_load_explicit(&tcb->is_cancelled, memory_order_acquire);
|
||||||
|
if(is_cancelled) {
|
||||||
|
u32 tgid = sys_getpid();
|
||||||
|
sys_exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rt_threads_setup() {
|
||||||
|
u64 sigs[3] = {(1ul << SIGCANCEL)};
|
||||||
|
struct sigaction handler = {
|
||||||
|
.sa_sigaction = thread_sigcancel_handler,
|
||||||
|
.sa_flags = SA_SIGINFO|SA_RESTART,
|
||||||
|
.sa_mask = 0xfffffffc7ffffffful,
|
||||||
|
};
|
||||||
|
syscall(SYS_rt_sigaction, SIGCANCEL, &handler, NULL, 1 * sizeof(u64));
|
||||||
|
}
|
||||||
|
|
||||||
static _RT_Status _rt_thread_current(_RT_Thread *thread) {
|
static _RT_Status _rt_thread_current(_RT_Thread *thread) {
|
||||||
thread->handle = (void *)((u64)__builtin_frame_address(0) & ~(cia_stack_size - 1));
|
thread->handle = (void *)((u64)__builtin_frame_address(0) & ~(cia_stack_size - 1));
|
||||||
return _RT_STATUS_OK;
|
return _RT_STATUS_OK;
|
||||||
|
@ -23,76 +44,86 @@ static _RT_Status _rt_thread_create(_RT_Thread *thread, int (*thread_fn)(void *c
|
||||||
return _RT_ERROR_GENERIC;
|
return _RT_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
void *stack = (u8*)stack_base + 2*cia_stack_size;
|
void *stack = (u8*)stack_base + 2*cia_stack_size;
|
||||||
// Find the TLS base and initialize the tls
|
// Find the TCB base and initialize the TCB
|
||||||
_LD_Thread_Block *tcb = (void *)((u64)((u8 *)stack - 1) & ~(cia_stack_size - 1));
|
_LD_Thread_Block *tcb = (void *)((u64)((u8 *)stack - 1) & ~(cia_stack_size - 1));
|
||||||
tcb->stack_canary = 0x12345678deadbeef;
|
|
||||||
u8 *tls_base = (u8 *)tcb - cia_tls_image_size;
|
u8 *tls_base = (u8 *)tcb - cia_tls_image_size;
|
||||||
for(int i = 0; i < cia_tls_image_size; ++i) {
|
for(int i = 0; i < cia_tls_image_size; ++i) {
|
||||||
tls_base[i] = ((u8 *)cia_tls_image_base)[i];
|
tls_base[i] = ((u8 *)cia_tls_image_base)[i];
|
||||||
}
|
}
|
||||||
// Initialize the _RT_Thread handle, which would point to
|
tcb->stack_canary = 0x12345678deadbeef;
|
||||||
// the TCB
|
tcb->next_tcb = NULL;
|
||||||
|
tcb->prev_tcb = thread_blocks_head;
|
||||||
|
atomic_store_explicit(&tcb->is_cancelled, 0, memory_order_relaxed);
|
||||||
|
if(thread_blocks_head != NULL) {
|
||||||
|
thread_blocks_head->next_tcb = tcb;
|
||||||
|
}
|
||||||
|
thread_blocks_head = tcb;
|
||||||
|
// This futex is reset on thread finish
|
||||||
|
tcb->state_finish = 1;
|
||||||
|
// Initialize the _RT_Thread handle, which would point to the TCB
|
||||||
thread->handle = tcb;
|
thread->handle = tcb;
|
||||||
atomic_store_explicit(&tcb->thread_finished, 0, memory_order_relaxed);
|
|
||||||
// Create the new thread
|
// Create the new thread
|
||||||
u64 flags = 0;
|
u64 flags = 0;
|
||||||
// flags |= CLONE_CHILD_CLEARTID;
|
flags |= CLONE_CHILD_CLEARTID;
|
||||||
// flags |= CLONE_PARENT_SETTID;
|
flags |= CLONE_PARENT_SETTID;
|
||||||
flags |= CLONE_FS;
|
flags |= CLONE_FS;
|
||||||
flags |= CLONE_FILES;
|
flags |= CLONE_FILES;
|
||||||
flags |= CLONE_SIGHAND;
|
flags |= CLONE_SIGHAND;
|
||||||
flags |= CLONE_THREAD;
|
flags |= CLONE_THREAD;
|
||||||
flags |= CLONE_VM;
|
flags |= CLONE_VM;
|
||||||
flags |= CLONE_SYSVSEM;
|
flags |= CLONE_SYSVSEM;
|
||||||
int *child_tid = (int *)&tcb->thread_id;
|
i64 ret = _rt_thread_start(flags, stack, &tcb->thread_id, &tcb->state_finish, 0, thread_fn, ctx);
|
||||||
int *parent_tid = (int *)&tcb->parent_id;
|
|
||||||
*child_tid = 1;
|
|
||||||
*parent_tid = 0;
|
|
||||||
i64 ret = _rt_thread_start(flags, stack, parent_tid, child_tid, 0, thread_fn, ctx);
|
|
||||||
if(ret < 0) {
|
if(ret < 0) {
|
||||||
return _RT_ERROR_GENERIC;
|
return _RT_ERROR_GENERIC;
|
||||||
}
|
}
|
||||||
return _RT_STATUS_OK;
|
return _RT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _rt_thread_setup(int (*thread_fn)(void *ctx), void *ctx) {
|
||||||
|
// struct sigaction handler = {
|
||||||
|
// .sa_sigaction = thread_sigcancel_handler,
|
||||||
|
// .sa_flags = SA_SIGINFO|SA_RESTART,
|
||||||
|
// .sa_mask = 0,
|
||||||
|
// };
|
||||||
|
// i32 result = sys_sigaction(SIGCANCEL, &handler, NULL);
|
||||||
|
return thread_fn(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
void _rt_thread_finish(int exit_code) {
|
void _rt_thread_finish(int exit_code) {
|
||||||
_LD_Thread_Block *tcb = (void *)((u64)__builtin_frame_address(0) & ~(cia_stack_size - 1));
|
_LD_Thread_Block *tcb = (void *)((u64)__builtin_frame_address(0) & ~(cia_stack_size - 1));
|
||||||
// Wait until the main thread decides what to do with the child thread
|
// Wait until the main thread decides what to do with the child thread
|
||||||
u32 thread_behaviour = atomic_load_explicit(&tcb->thread_behaviour, memory_order_relaxed);
|
u32 thread_state = atomic_load_explicit(&tcb->state_detach, memory_order_relaxed);
|
||||||
while(thread_behaviour == _LD_THREAD_BEHAVIOUR_NOT_SET) {
|
while(thread_state == _LD_THREAD_STATE_NOT_YET) {
|
||||||
syscall(SYS_futex, &tcb->thread_behaviour, FUTEX_WAIT, _LD_THREAD_BEHAVIOUR_NOT_SET, NULL, 0, 0);
|
syscall(SYS_futex, &tcb->state_detach, FUTEX_WAIT, _LD_THREAD_STATE_NOT_YET, NULL, 0, 0);
|
||||||
thread_behaviour = atomic_load_explicit(&tcb->thread_behaviour, memory_order_relaxed);
|
thread_state = atomic_load_explicit(&tcb->state_detach, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
// If main thread set this thread to be joined, we signal it that we're done here
|
if(_LD_THREAD_STATE_DETACHED) {
|
||||||
if(thread_behaviour == _LD_THREAD_BEHAVIOUR_JOIN) {
|
|
||||||
atomic_store_explicit(&tcb->exit_code, exit_code, memory_order_relaxed);
|
|
||||||
atomic_store_explicit(&tcb->thread_finished, 1, memory_order_release);
|
|
||||||
syscall(SYS_futex, &tcb->thread_finished, FUTEX_WAKE, 0, NULL, 0, 0);
|
|
||||||
sys_exit(exit_code);
|
|
||||||
}
|
|
||||||
else if(thread_behaviour == _LD_THREAD_BEHAVIOUR_DETACH) {
|
|
||||||
// TODO: clean up the thread resources
|
// TODO: clean up the thread resources
|
||||||
sys_exit(exit_code);
|
|
||||||
}
|
}
|
||||||
|
tcb->exit_code = exit_code;
|
||||||
|
sys_exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _RT_Status _rt_thread_join(_RT_Thread *thread, int *out_exit_code) {
|
static _RT_Status _rt_thread_join(_RT_Thread *thread, int *out_exit_code) {
|
||||||
_LD_Thread_Block *tcb = thread->handle;
|
_LD_Thread_Block *tcb = thread->handle;
|
||||||
// Signal the thread that we want it to be joined
|
// Signal the thread that we want it to be joined
|
||||||
atomic_store_explicit(&tcb->thread_behaviour, _LD_THREAD_BEHAVIOUR_JOIN, memory_order_relaxed);
|
atomic_store_explicit(&tcb->state_detach, _LD_THREAD_STATE_JOINED, memory_order_relaxed);
|
||||||
syscall(SYS_futex, &tcb->thread_behaviour, FUTEX_WAKE, 0, NULL, 0, 0);
|
syscall(SYS_futex, &tcb->state_detach, FUTEX_WAKE, 0, NULL, 0, 0);
|
||||||
// Wait until the thread signals that it has completed the execution
|
// Wait until the thread signals that it has completed the execution
|
||||||
while(atomic_load_explicit(&tcb->thread_finished, memory_order_acquire) != 1) {
|
while(tcb->state_finish != 0) {
|
||||||
syscall(SYS_futex, &tcb->thread_finished, FUTEX_WAIT, 0, NULL, 0, 0);
|
syscall(SYS_futex, &tcb->state_finish, FUTEX_WAIT, 0, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
// Set the exit code
|
// Set the exit code
|
||||||
*out_exit_code = atomic_load_explicit(&tcb->exit_code, memory_order_acquire);
|
// NOTE(bumbread): this is not a bug, because calling thrd_detach from one thread
|
||||||
|
// and thrd_join on the same thrd_t, from a different thread is supposed to be UB.
|
||||||
|
*out_exit_code = tcb->exit_code;
|
||||||
|
// TODO(bumbread): thread cleanup: destroy the TCB and thread-local storage
|
||||||
return _RT_STATUS_OK;
|
return _RT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _RT_Status _rt_thread_detach(_RT_Thread *thread) {
|
static _RT_Status _rt_thread_detach(_RT_Thread *thread) {
|
||||||
_LD_Thread_Block *tcb = thread->handle;
|
_LD_Thread_Block *tcb = thread->handle;
|
||||||
atomic_store_explicit(&tcb->thread_behaviour, _LD_THREAD_BEHAVIOUR_DETACH, memory_order_relaxed);
|
atomic_store_explicit(&tcb->state_detach, _LD_THREAD_STATE_DETACHED, memory_order_relaxed);
|
||||||
return _RT_STATUS_OK;
|
return _RT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +137,12 @@ static _RT_Status _rt_thread_yield() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static _RT_Status _rt_thread_terminate(_RT_Thread *thread) {
|
static _RT_Status _rt_thread_terminate(_RT_Thread *thread) {
|
||||||
return _RT_ERROR_NOT_IMPLEMENTED;
|
u32 tgid = sys_getpid();
|
||||||
|
_LD_Thread_Block *tcb = thread->handle;
|
||||||
|
atomic_store_explicit(&tcb->is_cancelled, 1, memory_order_release);
|
||||||
|
sys_tkill(tcb->thread_id, SIGCANCEL);
|
||||||
|
// syscall(SYS_rt_tgsigqueueinfo, tgid, tcb->thread_id, SIGCANCEL, NULL);
|
||||||
|
return _RT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _RT_Status _rt_thread_sleep(u64 time) {
|
static _RT_Status _rt_thread_sleep(u64 time) {
|
||||||
|
@ -116,3 +152,17 @@ static _RT_Status _rt_thread_sleep(u64 time) {
|
||||||
static _RT_Status _rt_thread_get_timer_freq(u64 *freq) {
|
static _RT_Status _rt_thread_get_timer_freq(u64 *freq) {
|
||||||
return _RT_ERROR_NOT_IMPLEMENTED;
|
return _RT_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _RT_Status _rt_thread_cancell_all_running() {
|
||||||
|
u32 tgid = sys_getpid();
|
||||||
|
_LD_Thread_Block *tcb_cur = thread_blocks_head;
|
||||||
|
while(tcb_cur != NULL) {
|
||||||
|
_LD_Thread_Block *tcb_next = tcb_cur->next_tcb;
|
||||||
|
u32 thread_id = tcb_cur->thread_id;
|
||||||
|
atomic_store_explicit(&tcb_cur->is_cancelled, 1, memory_order_release);
|
||||||
|
//sys_tgkill(tgid, thread_id, SIGCANCEL);
|
||||||
|
//SYS_rt_tgsigqueueinfo(tgid)
|
||||||
|
tcb_cur = tcb_next;
|
||||||
|
}
|
||||||
|
return _RT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -138,14 +138,24 @@ int printf(const char *restrict fmt, ...) {
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(fmt[i] == 'u') {
|
else if(fmt[i] == 'u') {
|
||||||
u32 arg;
|
u64 arg;
|
||||||
if(int_arg_size == 32) {
|
if(int_arg_size == 32) {
|
||||||
arg = va_arg(args, u32);
|
arg = va_arg(args, u32);
|
||||||
}
|
}
|
||||||
else if(int_arg_size == 64) {
|
else if(int_arg_size == 64) {
|
||||||
arg = va_arg(args, u64);
|
arg = va_arg(args, u64);
|
||||||
}
|
}
|
||||||
arg_chars = print_uint((u64)arg);
|
arg_chars = print_uint(arg);
|
||||||
|
}
|
||||||
|
else if(fmt[i] == 'x') {
|
||||||
|
u64 arg;
|
||||||
|
if(int_arg_size == 32) {
|
||||||
|
arg = va_arg(args, u32);
|
||||||
|
}
|
||||||
|
else if(int_arg_size == 64) {
|
||||||
|
arg = va_arg(args, u64);
|
||||||
|
}
|
||||||
|
print_hex(arg);
|
||||||
}
|
}
|
||||||
else if(fmt[i] == 'p') {
|
else if(fmt[i] == 'p') {
|
||||||
void *arg = va_arg(args, void *);
|
void *arg = va_arg(args, void *);
|
||||||
|
|
|
@ -25,4 +25,9 @@ int thrd_join(thrd_t thr, int *out_exit_code) {
|
||||||
|
|
||||||
void thrd_yield(void) {
|
void thrd_yield(void) {
|
||||||
_rt_thread_yield();
|
_rt_thread_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove later */
|
||||||
|
void thrd_terminate(thrd_t thr) {
|
||||||
|
_rt_thread_terminate(&thr.thread);
|
||||||
}
|
}
|
|
@ -4,25 +4,33 @@
|
||||||
#include <threads.h>
|
#include <threads.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <cia/sync.h>
|
#include <cia/sync.h>
|
||||||
|
#include <cia/mem.h>
|
||||||
|
|
||||||
|
#include <cia-ld/tcb.h>
|
||||||
|
|
||||||
|
thrd_t thrd;
|
||||||
static Cia_Mutex g_mutex;
|
static Cia_Mutex g_mutex;
|
||||||
static volatile i64 counter = 0;
|
static volatile i64 counter = 0;
|
||||||
|
|
||||||
int thrd_func(void *arg) {
|
int thrd_func(void *arg) {
|
||||||
|
_LD_Thread_Block *tcb = thrd.thread.handle;
|
||||||
|
printf("child thread TID: %I64d\n", tcb->thread_id);
|
||||||
printf("child thread: ok!\n");
|
printf("child thread: ok!\n");
|
||||||
for(int i = 0; i < 100000; ++i) {
|
for(int i = 0; i < 100000; ++i) {
|
||||||
cia_mutex_lock(&g_mutex);
|
cia_mutex_lock(&g_mutex);
|
||||||
counter += 1;
|
counter += 1;
|
||||||
cia_mutex_unlock(&g_mutex);
|
cia_mutex_unlock(&g_mutex);
|
||||||
}
|
}
|
||||||
|
for(;;);
|
||||||
printf("child thread: counter = %I64d\n", counter);
|
printf("child thread: counter = %I64d\n", counter);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
_LD_Thread_Block *tcb = (void *)((u64)__builtin_frame_address(0) & ~(2*MB - 1));
|
||||||
|
printf("main thread ID: %I64x\n", tcb->thread_id);
|
||||||
printf("main thread: before\n");
|
printf("main thread: before\n");
|
||||||
cia_mutex_init(&g_mutex);
|
cia_mutex_init(&g_mutex);
|
||||||
thrd_t thrd;
|
|
||||||
int status = thrd_create(&thrd, thrd_func, NULL);
|
int status = thrd_create(&thrd, thrd_func, NULL);
|
||||||
if(status == thrd_error) {
|
if(status == thrd_error) {
|
||||||
printf("main thread: error creating child thread\n");
|
printf("main thread: error creating child thread\n");
|
||||||
|
@ -35,7 +43,9 @@ int main() {
|
||||||
cia_mutex_unlock(&g_mutex);
|
cia_mutex_unlock(&g_mutex);
|
||||||
}
|
}
|
||||||
int exit_code;
|
int exit_code;
|
||||||
thrd_join(thrd, &exit_code);
|
thrd_detach(thrd);
|
||||||
printf("main thread: counter = %I64d\n", counter);
|
printf("main thread: counter = %I64d\n", counter);
|
||||||
|
thrd_terminate(thrd);
|
||||||
|
printf("terminated child thread\n", counter);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue