From da546a0822b1995efe1832c9cc57aab62ccdcf65 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Thu, 19 Jan 2023 23:27:12 +0100 Subject: kernel: delay freeing reaped processes, slightly more strict states --- src/kernel/proc.c | 79 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 23 deletions(-) (limited to 'src/kernel/proc.c') diff --git a/src/kernel/proc.c b/src/kernel/proc.c index acefeb9..d105f66 100755 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -11,6 +11,7 @@ #include static struct process *process_first = NULL; +static struct process *process_forgotten = NULL; /* linked list */ struct process *process_current; static uint32_t next_pid = 1; @@ -18,6 +19,7 @@ static uint32_t next_pid = 1; /** Removes a process from the process tree. */ static void process_forget(struct process *p); +static void process_free_forgotten(void); static _Noreturn void process_switch(struct process *proc); @@ -186,39 +188,46 @@ void process_kill(struct process *p, int ret) { } if (p->state == PS_DYING) { - process_transition(p, PS_TOREAP); + if (p->parent && proc_alive(p->parent) && !p->noreap) { + process_transition(p, PS_TOREAP); + } else { + process_transition(p, PS_TOMBSTONE); + } } process_tryreap(p); if (p == process_first) { + process_free_forgotten(); shutdown(); } } void process_filicide(struct process *parent, int ret) { - // O(n^2), but doable in linear time - for (;;) { - struct process *p = parent->child; - while (p && p->state == PS_TOREAP) { - p = p->sibling; - } - if (!p) return; /* no more killable children */ - while (p->child) { - p = p->child; - } - if (p->parent != parent) { - /* prevent infinite loop */ + /* Kill deeper descendants. */ + struct process *child, *child2; + for (child = parent->child; child; child = child2) { + child2 = child->sibling; + + // O(n^2), but doable in linear time + while (child->child) { + struct process *p = child->child; + while (p->child) p = p->child; p->noreap = true; + process_kill(p, ret); } - process_kill(p, ret); + + if (proc_alive(child)) { + process_kill(child, ret); + } + child = NULL; } } void process_tryreap(struct process *dead) { - // TODO low try writing a linter that prevents other functions from using *dead afterwards struct process *parent; assert(dead && !proc_alive(dead)); parent = dead->parent; + if (parent) assert(parent->child); if (dead->state == PS_TOREAP) { if (!dead->noreap && parent && proc_alive(parent) && parent->state != PS_DYING) { @@ -242,10 +251,11 @@ void process_tryreap(struct process *dead) { if (dead->child) { struct process *p = dead->child; while (p->child) p = p->child; - assert(p->state != PS_TOREAP); - assert(p->state != PS_TOMBSTONE); - assert(proc_alive(p)); - + if (p->state == PS_TOREAP) { + assert(proc_alive(p->parent)); + } else { + assert(proc_alive(p)); + } return; /* keep the tombstone */ } @@ -253,7 +263,6 @@ void process_tryreap(struct process *dead) { assert(dead->refcount == 0); if (parent) { /* not applicable to init */ process_forget(dead); - kfree(dead); // TODO force gcc to optimize the tail call here if (!proc_alive(parent)) { @@ -266,6 +275,7 @@ void process_tryreap(struct process *dead) { static void process_forget(struct process *p) { assert(p->parent); assert(p->parent->child); + assert(!p->child); if (p->parent->child == p) { p->parent->child = p->sibling; @@ -278,6 +288,21 @@ static void process_forget(struct process *p) { } prev->sibling = p->sibling; } + + p->parent = NULL; + p->sibling = process_forgotten; + process_forgotten = p; + process_transition(p, PS_FORGOTTEN); +} + +static void process_free_forgotten(void) { + while (process_forgotten) { + struct process *p = process_forgotten; + process_forgotten = p->sibling; + + process_transition(p, PS_FREED); + kfree(p); + } } static _Noreturn void process_switch(struct process *proc) { @@ -291,6 +316,10 @@ static _Noreturn void process_switch(struct process *proc) { } _Noreturn void process_switch_any(void) { + /* At this point there will be no leftover pointers to forgotten + * processes on the stack, so it's safe to free them. */ + process_free_forgotten(); + for (;;) { if (process_current && process_current->state == PS_RUNNING) process_switch(process_current); @@ -409,9 +438,13 @@ handle_t process_handle_put(struct process *p, struct handle *h) { } void process_transition(struct process *p, enum process_state state) { - assert(p->state != PS_TOMBSTONE); - if (state == PS_TOMBSTONE) { - assert(p->state == PS_TOREAP); + assert(p->state != PS_FREED); + if (state == PS_FREED) { + assert(p->state == PS_FORGOTTEN); + } else if (state == PS_FORGOTTEN) { + assert(p->state == PS_TOMBSTONE); + } else if (state == PS_TOMBSTONE) { + assert(p->state == PS_TOREAP || p->state == PS_DYING); } else if (state == PS_TOREAP) { assert(p->state == PS_DYING); } else if (state != PS_RUNNING && state != PS_DYING) { -- cgit v1.2.3