[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.
This commit is contained in:
Justin C. Miller
2023-02-19 14:37:31 -08:00
parent 274891854f
commit 723f7d0330
6 changed files with 25 additions and 28 deletions

View File

@@ -68,6 +68,7 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps)
capability * capability *
cap_table::find_without_retain(j6_handle_t id) cap_table::find_without_retain(j6_handle_t id)
{ {
util::scoped_lock lock {m_lock};
return m_caps.find(id); return m_caps.find(id);
} }

View File

@@ -60,9 +60,10 @@ process::exit(int32_t code)
util::scoped_lock lock {m_threads_lock}; util::scoped_lock lock {m_threads_lock};
for (auto *thread : m_threads) { for (auto *thread : m_threads) {
if (thread != &current) if (thread != &current) {
thread->exit(); thread->exit();
} }
}
lock.release(); lock.release();
if (&current.parent() == this) if (&current.parent() == this)
@@ -82,6 +83,7 @@ process::create_thread(uintptr_t rsp3, uint8_t priority)
thread *th = new thread(*this, priority); thread *th = new thread(*this, priority);
kassert(th, "Failed to create thread!"); kassert(th, "Failed to create thread!");
th->handle_retain();
if (rsp3) if (rsp3)
th->tcb()->rsp3 = rsp3; th->tcb()->rsp3 = rsp3;
@@ -91,23 +93,25 @@ process::create_thread(uintptr_t rsp3, uint8_t priority)
return th; return th;
} }
bool void
process::thread_exited(thread *th) process::thread_exited(thread *th)
{ {
util::scoped_lock lock {m_threads_lock};
kassert(&th->m_parent == this, "Process got thread_exited for non-child!"); kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
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); m_threads.remove_swap(th);
remove_handle(th->self_handle());
delete th;
// TODO: delete the thread's stack VMA // TODO: delete the thread's stack VMA
if (m_threads.empty() && m_state != state::exited) { if (m_threads.empty()) {
lock.release();
exit(-1); exit(-1);
return true; }
} }
return false; th->handle_release();
} }
void void

View File

@@ -75,11 +75,7 @@ public:
/// Inform the process of an exited thread /// Inform the process of an exited thread
/// \args th The thread which has exited /// \args th The thread which has exited
/// \returns True if this thread ending has ended the process void thread_exited(thread *th);
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; }
/// Get the process object that owns kernel threads and the /// Get the process object that owns kernel threads and the
/// kernel address space /// kernel address space

View File

@@ -22,6 +22,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
m_wake_value {0}, m_wake_value {0},
m_wake_timeout {0} m_wake_timeout {0}
{ {
parent.handle_retain();
parent.space().initialize_tcb(m_tcb); parent.space().initialize_tcb(m_tcb);
m_tcb.priority = pri; m_tcb.priority = pri;
m_tcb.thread = this; m_tcb.thread = this;
@@ -37,6 +38,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
thread::~thread() thread::~thread()
{ {
g_kernel_stacks.return_section(m_tcb.kernel_stack); g_kernel_stacks.return_section(m_tcb.kernel_stack);
m_parent.handle_release();
} }
thread & thread::current() { return *current_cpu().thread; } thread & thread::current() { return *current_cpu().thread; }
@@ -99,6 +101,7 @@ thread::exit()
{ {
m_wake_timeout = 0; m_wake_timeout = 0;
set_state(state::exited); set_state(state::exited);
m_parent.thread_exited(this);
m_join_queue.clear(); m_join_queue.clear();
block(); block();
} }

View File

@@ -170,11 +170,6 @@ public:
/// \arg rsp The existing stack for the idle thread /// \arg rsp The existing stack for the idle thread
static thread * create_idle_thread(process &kernel, uintptr_t rsp); 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: private:
thread() = delete; thread() = delete;
thread(const thread &other) = delete; thread(const thread &other) = delete;

View File

@@ -111,6 +111,9 @@ scheduler::add_thread(TCB *t)
run_queue &queue = m_run_queues[cpu->index]; run_queue &queue = m_run_queues[cpu->index];
util::scoped_lock lock {queue.lock}; util::scoped_lock lock {queue.lock};
obj::thread *th = t->thread;
th->handle_retain();
t->cpu = cpu; t->cpu = cpu;
t->time_left = quantum(t->priority); t->time_left = quantum(t->priority);
queue.blocked.push_back(static_cast<tcb_node*>(t)); queue.blocked.push_back(static_cast<tcb_node*>(t));
@@ -147,12 +150,7 @@ scheduler::prune(run_queue &queue, uint64_t now)
if (current) continue; if (current) continue;
queue.blocked.remove(remove); queue.blocked.remove(remove);
process &p = th->parent(); th->handle_release();
// thread_exited deletes the thread, and returns true if the process
// should also now be deleted
if(!current && p.thread_exited(th))
delete &p;
} else { } else {
queue.blocked.remove(remove); queue.blocked.remove(remove);
log::spam(logs::sched, "Prune: readying unblocked thread %llx", th->koid()); log::spam(logs::sched, "Prune: readying unblocked thread %llx", th->koid());