From 723f7d0330c0e5ec1dfa8c1d13ce07ce58dba5a5 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 19 Feb 2023 14:37:31 -0800 Subject: [PATCH] [kernel] Delete processes & threads only via refcounts Previously processes and threads would be deleted by the scheduler. Now, only delete them based on refcounts - this allows joining an already-exited thread, for instance. --- src/kernel/capabilities.cpp | 1 + src/kernel/objects/process.cpp | 28 ++++++++++++++++------------ src/kernel/objects/process.h | 6 +----- src/kernel/objects/thread.cpp | 3 +++ src/kernel/objects/thread.h | 5 ----- src/kernel/scheduler.cpp | 10 ++++------ 6 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/kernel/capabilities.cpp b/src/kernel/capabilities.cpp index 1043678..2d7d4dd 100644 --- a/src/kernel/capabilities.cpp +++ b/src/kernel/capabilities.cpp @@ -68,6 +68,7 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps) capability * cap_table::find_without_retain(j6_handle_t id) { + util::scoped_lock lock {m_lock}; return m_caps.find(id); } diff --git a/src/kernel/objects/process.cpp b/src/kernel/objects/process.cpp index 325e58e..b99a3ce 100644 --- a/src/kernel/objects/process.cpp +++ b/src/kernel/objects/process.cpp @@ -60,8 +60,9 @@ process::exit(int32_t code) util::scoped_lock lock {m_threads_lock}; for (auto *thread : m_threads) { - if (thread != ¤t) + if (thread != ¤t) { thread->exit(); + } } lock.release(); @@ -82,6 +83,7 @@ process::create_thread(uintptr_t rsp3, uint8_t priority) thread *th = new thread(*this, priority); kassert(th, "Failed to create thread!"); + th->handle_retain(); if (rsp3) th->tcb()->rsp3 = rsp3; @@ -91,23 +93,25 @@ process::create_thread(uintptr_t rsp3, uint8_t priority) return th; } -bool +void process::thread_exited(thread *th) { - util::scoped_lock lock {m_threads_lock}; - kassert(&th->m_parent == this, "Process got thread_exited for non-child!"); - m_threads.remove_swap(th); - remove_handle(th->self_handle()); - delete th; - // TODO: delete the thread's stack VMA - if (m_threads.empty() && m_state != state::exited) { - exit(-1); - return true; + if (m_state != state::exited) { + // if we're already going away, just release + // the thread's handle and skip all this + util::scoped_lock lock {m_threads_lock}; + m_threads.remove_swap(th); + + // TODO: delete the thread's stack VMA + if (m_threads.empty()) { + lock.release(); + exit(-1); + } } - return false; + th->handle_release(); } void diff --git a/src/kernel/objects/process.h b/src/kernel/objects/process.h index 5afe2e6..667eedc 100644 --- a/src/kernel/objects/process.h +++ b/src/kernel/objects/process.h @@ -75,11 +75,7 @@ public: /// Inform the process of an exited thread /// \args th The thread which has exited - /// \returns True if this thread ending has ended the process - bool thread_exited(thread *th); - - /// Get the handle for this process to refer to itself - inline j6_handle_t self_handle() const { return m_self_handle; } + void thread_exited(thread *th); /// Get the process object that owns kernel threads and the /// kernel address space diff --git a/src/kernel/objects/thread.cpp b/src/kernel/objects/thread.cpp index 6dbcdb5..6d6ddad 100644 --- a/src/kernel/objects/thread.cpp +++ b/src/kernel/objects/thread.cpp @@ -22,6 +22,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) : m_wake_value {0}, m_wake_timeout {0} { + parent.handle_retain(); parent.space().initialize_tcb(m_tcb); m_tcb.priority = pri; m_tcb.thread = this; @@ -37,6 +38,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) : thread::~thread() { g_kernel_stacks.return_section(m_tcb.kernel_stack); + m_parent.handle_release(); } thread & thread::current() { return *current_cpu().thread; } @@ -99,6 +101,7 @@ thread::exit() { m_wake_timeout = 0; set_state(state::exited); + m_parent.thread_exited(this); m_join_queue.clear(); block(); } diff --git a/src/kernel/objects/thread.h b/src/kernel/objects/thread.h index c5c005d..22d5c60 100644 --- a/src/kernel/objects/thread.h +++ b/src/kernel/objects/thread.h @@ -170,11 +170,6 @@ public: /// \arg rsp The existing stack for the idle thread static thread * create_idle_thread(process &kernel, uintptr_t rsp); -protected: - /// Don't delete a thread on no handles, the scheduler takes - /// care of that. - virtual void on_no_handles() override {} - private: thread() = delete; thread(const thread &other) = delete; diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 5eb4d15..21c6183 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -111,6 +111,9 @@ scheduler::add_thread(TCB *t) run_queue &queue = m_run_queues[cpu->index]; util::scoped_lock lock {queue.lock}; + obj::thread *th = t->thread; + th->handle_retain(); + t->cpu = cpu; t->time_left = quantum(t->priority); queue.blocked.push_back(static_cast(t)); @@ -147,12 +150,7 @@ scheduler::prune(run_queue &queue, uint64_t now) if (current) continue; queue.blocked.remove(remove); - process &p = th->parent(); - - // thread_exited deletes the thread, and returns true if the process - // should also now be deleted - if(!current && p.thread_exited(th)) - delete &p; + th->handle_release(); } else { queue.blocked.remove(remove); log::spam(logs::sched, "Prune: readying unblocked thread %llx", th->koid());