[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 *
cap_table::find_without_retain(j6_handle_t id)
{
util::scoped_lock lock {m_lock};
return m_caps.find(id);
}

View File

@@ -60,9 +60,10 @@ process::exit(int32_t code)
util::scoped_lock lock {m_threads_lock};
for (auto *thread : m_threads) {
if (thread != &current)
if (thread != &current) {
thread->exit();
}
}
lock.release();
if (&current.parent() == this)
@@ -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!");
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);
remove_handle(th->self_handle());
delete th;
// TODO: delete the thread's stack VMA
if (m_threads.empty() && m_state != state::exited) {
if (m_threads.empty()) {
lock.release();
exit(-1);
return true;
}
}
return false;
th->handle_release();
}
void

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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<tcb_node*>(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());