mirror of
https://github.com/justinian/jsix.git
synced 2025-12-11 08:54:31 -08:00
Now that kutil has no kernel-specific code in it anymore, it can actually be linked to by anything, so I'm renaming it 'util'. Also, I've tried to unify the way that the system libraries from src/libraries are #included using <> instead of "". Other small change: util::bip_buffer got a spinlock to guard against state corruption.
150 lines
3.3 KiB
C++
150 lines
3.3 KiB
C++
#include <util/no_construct.h>
|
|
|
|
#include "assert.h"
|
|
#include "cpu.h"
|
|
#include "objects/process.h"
|
|
#include "objects/thread.h"
|
|
#include "objects/vm_area.h"
|
|
#include "scheduler.h"
|
|
|
|
// This object is initialized _before_ global constructors are called,
|
|
// so we don't want it to have a global constructor at all, lest it
|
|
// overwrite the previous initialization.
|
|
static util::no_construct<process> __g_kernel_process_storage;
|
|
process &g_kernel_process = __g_kernel_process_storage.value;
|
|
|
|
|
|
process::process() :
|
|
kobject {kobject::type::process},
|
|
m_next_handle {1},
|
|
m_state {state::running}
|
|
{
|
|
j6_handle_t self = add_handle(this);
|
|
kassert(self == self_handle(), "Process self-handle is not 1");
|
|
}
|
|
|
|
// The "kernel process"-only constructor
|
|
process::process(page_table *kpml4) :
|
|
kobject {kobject::type::process},
|
|
m_space {kpml4},
|
|
m_next_handle {self_handle()+1},
|
|
m_state {state::running}
|
|
{
|
|
}
|
|
|
|
process::~process()
|
|
{
|
|
for (auto &it : m_handles)
|
|
if (it.val) it.val->handle_release();
|
|
}
|
|
|
|
process & process::current() { return *current_cpu().process; }
|
|
process & process::kernel_process() { return g_kernel_process; }
|
|
|
|
process *
|
|
process::create_kernel_process(page_table *pml4)
|
|
{
|
|
return new (&g_kernel_process) process {pml4};
|
|
}
|
|
|
|
void
|
|
process::exit(int32_t code)
|
|
{
|
|
// TODO: make this thread-safe
|
|
m_state = state::exited;
|
|
m_return_code = code;
|
|
close();
|
|
|
|
for (auto *thread : m_threads) {
|
|
thread->exit(code);
|
|
}
|
|
|
|
if (this == current_cpu().process)
|
|
scheduler::get().schedule();
|
|
}
|
|
|
|
void
|
|
process::update()
|
|
{
|
|
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
|
|
|
size_t i = 0;
|
|
uint32_t status = 0;
|
|
while (i < m_threads.count()) {
|
|
thread *th = m_threads[i];
|
|
if (th->has_state(thread::state::exited)) {
|
|
status = th->m_return_code;
|
|
m_threads.remove_swap_at(i);
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (m_threads.count() == 0) {
|
|
// TODO: What really is the return code in this case?
|
|
exit(status);
|
|
}
|
|
}
|
|
|
|
thread *
|
|
process::create_thread(uintptr_t rsp3, uint8_t priority)
|
|
{
|
|
if (priority == default_priority)
|
|
priority = scheduler::default_priority;
|
|
|
|
thread *th = new thread(*this, priority);
|
|
kassert(th, "Failed to create thread!");
|
|
|
|
if (rsp3)
|
|
th->tcb()->rsp3 = rsp3;
|
|
|
|
m_threads.append(th);
|
|
scheduler::get().add_thread(th->tcb());
|
|
return th;
|
|
}
|
|
|
|
bool
|
|
process::thread_exited(thread *th)
|
|
{
|
|
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
|
uint32_t status = th->m_return_code;
|
|
m_threads.remove_swap(th);
|
|
remove_handle(th->self_handle());
|
|
delete th;
|
|
|
|
// TODO: delete the thread's stack VMA
|
|
|
|
if (m_threads.count() == 0) {
|
|
exit(status);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
j6_handle_t
|
|
process::add_handle(kobject *obj)
|
|
{
|
|
if (!obj)
|
|
return j6_handle_invalid;
|
|
|
|
obj->handle_retain();
|
|
j6_handle_t handle = m_next_handle++;
|
|
m_handles.insert(handle, obj);
|
|
return handle;
|
|
}
|
|
|
|
bool
|
|
process::remove_handle(j6_handle_t handle)
|
|
{
|
|
kobject *obj = m_handles.find(handle);
|
|
if (obj) obj->handle_release();
|
|
return m_handles.erase(handle);
|
|
}
|
|
|
|
kobject *
|
|
process::lookup_handle(j6_handle_t handle)
|
|
{
|
|
return m_handles.find(handle);
|
|
}
|