diff options
author | dzwdz | 2022-08-19 18:30:35 +0200 |
---|---|---|
committer | dzwdz | 2022-08-19 18:30:35 +0200 |
commit | 6bea8cd391125734339dfb83db498a8651c9f7f7 (patch) | |
tree | 1eb30cda98318ff4fc63652425ef725903619766 /src | |
parent | 7a4bc281958c639cd52ff4f192933aa161ba81a4 (diff) |
syscall/fork: allow sharing handles between threads
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/proc.c | 48 | ||||
-rw-r--r-- | src/kernel/proc.h | 4 | ||||
-rw-r--r-- | src/shared/include/camellia/flags.h | 1 | ||||
-rw-r--r-- | src/user/app/tests/kernel/threads.c | 25 | ||||
-rw-r--r-- | src/user/lib/thread.c | 2 |
5 files changed, 62 insertions, 18 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 77c861c..ebd7cce 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -26,6 +26,7 @@ struct process *process_seed(void *data, size_t datalen) { process_first->pages = pagedir_new(); process_first->mount = vfs_mount_seed(); process_first->id = next_pid++; + process_first->_handles = kzalloc(sizeof(struct handle) * HANDLE_MAX); // map the stack to the last page in memory // TODO move to user bootstrap @@ -87,15 +88,38 @@ struct process *process_fork(struct process *parent, int flags) { assert(child->mount); child->mount->refs++; - for (handle_t h = 0; h < HANDLE_MAX; h++) { - child->_handles[h] = parent->_handles[h]; - if (child->_handles[h]) - child->_handles[h]->refcount++; + if (flags & FORK_SHAREHANDLE) { + if (!parent->handles_refcount) { + parent->handles_refcount = kmalloc(sizeof *parent->handles_refcount); + *parent->handles_refcount = 1; + } + *parent->handles_refcount += 1; + child->handles_refcount = parent->handles_refcount; + child->_handles = parent->_handles; + } else { + child->_handles = kzalloc(sizeof(struct handle) * HANDLE_MAX); + for (handle_t h = 0; h < HANDLE_MAX; h++) { + child->_handles[h] = parent->_handles[h]; + if (child->_handles[h]) + child->_handles[h]->refcount++; + } } return child; } +/* meant to be used with p->*_refcount */ +static bool unref(uint64_t *refcount) { + if (!refcount) return true; + assert(*refcount != 0); + *refcount -= 1; + if (*refcount == 0) { + kfree(refcount); + return true; + } + return false; +} + void process_kill(struct process *p, int ret) { if (p->state != PS_DEAD) { if (p->handled_req) { @@ -147,8 +171,11 @@ void process_kill(struct process *p, int ret) { if (p->state == PS_WAITS4TIMER) timer_deschedule(p); - for (handle_t hid = 0; hid < HANDLE_MAX; hid++) - process_handle_close(p, hid); + if (unref(p->handles_refcount)) { + for (handle_t hid = 0; hid < HANDLE_MAX; hid++) + process_handle_close(p, hid); + kfree(p->_handles); + } vfs_mount_remref(p->mount); p->mount = NULL; @@ -161,14 +188,7 @@ void process_kill(struct process *p, int ret) { p->execbuf.buf = NULL; } - if (p->pages_refcount) { - assert(*p->pages_refcount != 0); - *p->pages_refcount -= 1; - if (*p->pages_refcount == 0) { - kfree(p->pages_refcount); - pagedir_free(p->pages); - } - } else { + if (unref(p->pages_refcount)) { pagedir_free(p->pages); } diff --git a/src/kernel/proc.h b/src/kernel/proc.h index 4b256bd..f445eab 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -48,7 +48,9 @@ struct process { }; struct vfs_mount *mount; - struct handle *_handles[HANDLE_MAX]; + struct handle **_handles; /* points to struct handle *[HANDLE_MAX] */ + uint64_t *handles_refcount; /* works just like pages_refcount */ + uint32_t id; /* only for debugging, don't expose to userland */ bool noreap; diff --git a/src/shared/include/camellia/flags.h b/src/shared/include/camellia/flags.h index 30762bd..0eaa02e 100644 --- a/src/shared/include/camellia/flags.h +++ b/src/shared/include/camellia/flags.h @@ -6,6 +6,7 @@ #define FORK_NOREAP 1 #define FORK_NEWFS 2 #define FORK_SHAREMEM 4 +#define FORK_SHAREHANDLE 8 #define WRITE_TRUNCATE 1 diff --git a/src/user/app/tests/kernel/threads.c b/src/user/app/tests/kernel/threads.c index 9f08c39..2964883 100644 --- a/src/user/app/tests/kernel/threads.c +++ b/src/user/app/tests/kernel/threads.c @@ -1,16 +1,15 @@ #include "../tests.h" #include <camellia/flags.h> #include <camellia/syscalls.h> +#include <string.h> #include <user/lib/esemaphore.h> #include <user/lib/thread.h> int global_n; - static void basic_thread(void *sem) { global_n = 10; esem_signal(sem); } - static void test_basic_thread(void) { struct evil_sem *sem = esem_new(0); global_n = 0; @@ -19,6 +18,28 @@ static void test_basic_thread(void) { test(global_n == 10); } + +handle_t global_h; +static void shared_handle(void *sem) { + handle_t ends[2]; + test(_syscall_pipe(ends, 0) >= 0); + global_h = ends[0]; + esem_signal(sem); + _syscall_write(ends[1], "Hello!", 7, -1, 0); +} +static void test_shared_handle(void) { + struct evil_sem *sem = esem_new(0); + char buf[16]; + global_h = -1; + thread_create(FORK_NOREAP, shared_handle, sem); + esem_wait(sem); + + test(global_h >= 0); + test(_syscall_read(global_h, buf, sizeof buf, 0) == 7); + test(!strcmp("Hello!", buf)); +} + void r_k_threads(void) { run_test(test_basic_thread); + run_test(test_shared_handle); } diff --git a/src/user/lib/thread.c b/src/user/lib/thread.c index 25d98a9..f7353ad 100644 --- a/src/user/lib/thread.c +++ b/src/user/lib/thread.c @@ -4,7 +4,7 @@ #include <user/lib/thread.h> void thread_create(int flags, void (*fn)(void*), void *arg) { - if (!_syscall_fork(flags | FORK_SHAREMEM, NULL)) { + if (!_syscall_fork(flags | FORK_SHAREMEM | FORK_SHAREHANDLE, NULL)) { void *stack = malloc(4096); chstack(arg, fn, stack + 4096); } |