From fdbc5adf83741d7e3af0c3f0ffdb59bdf42493b6 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 4 Jul 2024 22:52:47 +0300 Subject: generalize orphants + Allow threads to make themselves become orphants --- src/orphanage.c | 13 +++++++------ src/tcb.c | 12 +++++++----- src/uapi/dispatch.c | 1 + src/uapi/ipc.c | 19 +++++++++++-------- src/uapi/proc.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/orphanage.c b/src/orphanage.c index 7415e8b..4760f73 100644 --- a/src/orphanage.c +++ b/src/orphanage.c @@ -14,17 +14,17 @@ bool orphan(struct tcb *t) { - struct tcb *r = get_rproc(t); - return !r || r->dead; + return is_set(t->state, TCB_ORPHAN); } void orphanize(struct tcb *t) { - catastrophic_assert(!is_rpc(t)); + set_bit(t->state, TCB_ORPHAN); +} - struct tcb *r = get_tcb(t->rid); - if (r) - unreference_proc(r); +void unorphanize(struct tcb *t) +{ + catastrophic_assert(!is_rpc(t)); /* attach to init process */ struct tcb *init = get_tcb(1); @@ -35,6 +35,7 @@ void orphanize(struct tcb *t) t->eid = 1; t->proc = init->proc; + t->rpc_stack = RPC_STACK_BASE; use_vmem(t->proc.vmem); catastrophic_assert(init->callback); diff --git a/src/tcb.c b/src/tcb.c index 73711c3..1287cef 100644 --- a/src/tcb.c +++ b/src/tcb.c @@ -127,7 +127,7 @@ struct tcb *create_thread(struct tcb *p) id_t tid = __alloc_tid(t); tcbs[tid] = t; t->tid = tid; - t->dead = false; + t->state = 0; if (likely(p)) { t->pid = p->pid; @@ -235,6 +235,10 @@ stat_t destroy_thread(struct tcb *t) unqueue_ipi(t); + /** @todo timers, irqs? theoretically we could allow them to stay and + * let the handler check if the thread is still interested in the + * interrupt */ + /* someone still relies on us existing, don't actually free thread data * quite yet */ if (t->refcount) @@ -255,9 +259,7 @@ stat_t destroy_proc(struct tcb *p) * the segfault handler if the thread has become orphaned. * Currently no segfault handler exists, though. */ - p->dead = true; - /* unreference ourselves */ - unreference_proc(p); + set_bits(p->state, TCB_ZOMBIE); /* clear all privately owned memory regions, keep shared ones alive for * now */ @@ -284,7 +286,7 @@ void unreference_proc(struct tcb *p) hard_assert(is_proc(p), RETURN_VOID); p->refcount--; - if (p->dead && p->refcount == 0) { + if (zombie(p) && p->refcount == 0) { dbg("thread %d is completely destroyed\n", p->tid); __destroy_thread_data(p); } diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c index 7cfce4c..c4ad811 100644 --- a/src/uapi/dispatch.c +++ b/src/uapi/dispatch.c @@ -81,6 +81,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break; case SYS_SLEEP: sys_sleep(t, a, b, c, d, e); break; case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break; + case SYS_DETACH: sys_detach(t, a, b, c, d, e); break; case SYS_EXIT: sys_exit(t, a, b, c, d, e); break; default: error("Syscall %zu outside allowed range [0 - %i]\n", syscall, diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c index f3896f5..bc49dbb 100644 --- a/src/uapi/ipc.c +++ b/src/uapi/ipc.c @@ -83,7 +83,7 @@ static vm_t enter_rpc(struct tcb *t, struct sys_ret a, { /* reuse current rpc stack location if we're being kicked */ vm_t rpc_stack = (kind == IPC_KICK && - is_rpc(t)) ? t->rpc_stack :rpc_position(t); + is_rpc(t)) ? t->rpc_stack : rpc_position(t); struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; ctx->regs = t->regs; @@ -179,8 +179,8 @@ void notify(struct tcb *t, enum notify_flag flag) return; struct tcb *r = get_tcb(t->notify_id); - if (!r || !r->callback) { - error("notify callback dead\n"); + if (!r || r->state || !r->callback) { + error("notify callback unavailable\n"); t->notify_flags = 0; return; } @@ -218,31 +218,34 @@ static void leave_rpc(struct tcb *t, struct sys_ret a) { vm_t rpc_stack = t->rpc_stack + BASE_PAGE_SIZE; struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; - vm_t top = ctx->rpc_stack; t->regs = ctx->regs; /* again, get rid of args as fast as possible */ set_args(t, 6, a); struct tcb *r = get_tcb(ctx->pid); - while (!r || r->dead) { + while (!r || !is_proc(r) || zombie(r)) { /* we unwound back to our root process which is apparently dead, * we're orphaned :( */ - if (ctx->pid == t->rid) { + if (rpc_stack_empty(ctx->rpc_stack)) { orphanize(t); - return; + break; } rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE; ctx = (struct call_ctx *)(rpc_stack) - 1; r = get_tcb(ctx->pid); + set_args1(t, ERR_NF); } + if (orphan(t) && !is_rpc(t)) + unorphanize(t); + set_return(t, ctx->exec); /* if we're returning from a failed rpc, this should essentially be a * no-op */ - mark_rpc_valid(t, top); + mark_rpc_valid(t, ctx->rpc_stack); t->rpc_stack = ctx->rpc_stack; t->pid = ctx->pid; t->eid = ctx->eid; diff --git a/src/uapi/proc.c b/src/uapi/proc.c index 3ce808f..231cc32 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -234,6 +234,35 @@ SYSCALL_DEFINE1(exit)(struct tcb *t, sys_arg_t tid) } } +/** + * Syscall handler for orphanizing a thread. + * + * @param t Thread that wants to make itself an orphant. + * @return \ref OK on success, + * \ref ERR_PERM if current process missing \ref CAP_PROC, + * \ref ERR_INVAL if already an orphant. + */ +SYSCALL_DEFINE0(detach)(struct tcb *t) +{ + struct tcb *c = get_cproc(t); + if (!(has_cap(c->caps, CAP_PROC))) + return_args1(t, ERR_PERM); + + if (orphan(t)) + return_args1(t, ERR_INVAL); + + struct tcb *r = get_tcb(t->rid); + if (r) + unreference_proc(r); + + orphanize(t); + + if (!is_rpc(t)) + unorphanize(t); + + return_args1(t, OK); +} + /** * Swap syscall handler. * -- cgit v1.3