summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kernel/proc.c28
-rw-r--r--src/kernel/proc.h4
-rw-r--r--src/kernel/syscalls.c31
3 files changed, 38 insertions, 25 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index df20df3..94fc419 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -1,5 +1,6 @@
#include <kernel/arch/generic.h>
#include <kernel/mem/alloc.h>
+#include <kernel/mem/virt.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
#include <kernel/util.h>
@@ -101,3 +102,30 @@ handle_t process_find_handle(struct process *proc) {
if (handle == HANDLE_MAX) handle = -1;
return handle;
}
+
+int process_try2collect(struct process *dead) {
+ struct process *parent = dead->parent;
+ int len, ret;
+ bool res;
+
+ assert(dead->state == PS_DEAD);
+
+ switch (parent->state) {
+ case PS_WAITS4CHILDDEATH:
+ dead->state = PS_DEADER;
+ parent->state = PS_RUNNING;
+
+ len = parent->death_msg.len < dead->death_msg.len
+ ? parent->death_msg.len : dead->death_msg.len;
+ res = virt_cpy(
+ parent->pages, parent->death_msg.buf,
+ dead->pages, dead->death_msg.buf, len);
+
+ ret = res ? len : 0;
+ regs_savereturn(&parent->regs, ret);
+ return ret;
+
+ default:
+ return -1;
+ }
+}
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index 0e02d27..82bb59a 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -55,3 +55,7 @@ _Noreturn void process_switch_any(void); // switches to any running process
struct process *process_find(enum process_state);
handle_t process_find_handle(struct process *proc); // finds the first free handle
+
+/** Tries to transistion from PS_DEAD to PS_DEADER.
+ * @return a nonnegative length of the quit message if successful, a negative val otherwise*/
+int process_try2collect(struct process *dead);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index ac21cdb..16238e7 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -8,33 +8,11 @@
#include <shared/syscalls.h>
#include <stdint.h>
-_Noreturn static void await_finish(struct process *dead, struct process *listener) {
- int len;
- bool res;
-
- assert(dead->state == PS_DEAD);
- assert(listener->state == PS_WAITS4CHILDDEATH);
- dead->state = PS_DEADER;
- listener->state = PS_RUNNING;
-
- len = listener->death_msg.len < dead->death_msg.len
- ? listener->death_msg.len : dead->death_msg.len;
- res = virt_cpy(
- listener->pages, listener->death_msg.buf,
- dead->pages, dead->death_msg.buf, len);
- regs_savereturn(&listener->regs, res ? len : -1);
- process_switch(listener);
-}
-
-
_Noreturn void _syscall_exit(const char __user *msg, size_t len) {
process_current->state = PS_DEAD;
process_current->death_msg.buf = (userptr_t) msg; // discarding const
process_current->death_msg.len = len;
-
- if (process_current->parent->state == PS_WAITS4CHILDDEATH)
- await_finish(process_current, process_current->parent);
-
+ process_try2collect(process_current);
process_switch_any();
}
@@ -47,8 +25,11 @@ int _syscall_await(char __user *buf, int len) {
// find any already dead children
for (struct process *iter = process_current->child;
iter; iter = iter->sibling) {
- if (iter->state == PS_DEAD)
- await_finish(iter, process_current); // doesn't return
+ if (iter->state == PS_DEAD) {
+ int ret = process_try2collect(iter);
+ assert(ret >= 0);
+ return ret;
+ }
if (iter->state != PS_DEADER)
has_children = true;
}