summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kernel/proc.c48
-rw-r--r--src/kernel/proc.h4
-rw-r--r--src/shared/include/camellia/flags.h1
-rw-r--r--src/user/app/tests/kernel/threads.c25
-rw-r--r--src/user/lib/thread.c2
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);
}