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/kernel | |
parent | 7a4bc281958c639cd52ff4f192933aa161ba81a4 (diff) |
syscall/fork: allow sharing handles between threads
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/proc.c | 48 | ||||
-rw-r--r-- | src/kernel/proc.h | 4 |
2 files changed, 37 insertions, 15 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; |