aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/orphanage.c13
-rw-r--r--src/tcb.c12
-rw-r--r--src/uapi/dispatch.c1
-rw-r--r--src/uapi/ipc.c19
-rw-r--r--src/uapi/proc.c29
5 files changed, 55 insertions, 19 deletions
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
@@ -235,6 +235,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.
*
* \todo Implement.