[kernel] Have thread call scheduler on blocking

Instead of making every callsite that may make a thread do a blocking
operation also invoke the scheduler, move that logic into thread
implementation - if the thread is blocking and is the current thread,
call schedule().

Related changes in this commit:

- Also make exiting threads and processes call the scheduler when
  blocking.
- Threads start blocked, and get automatically added to the scheduler's
  blocked list.
This commit is contained in:
2020-09-27 21:35:15 -07:00
parent ff78c951f0
commit 87b0a93d32
9 changed files with 35 additions and 38 deletions

View File

@@ -1,7 +1,6 @@
#include "objects/endpoint.h" #include "objects/endpoint.h"
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "scheduler.h"
#include "vm_space.h" #include "vm_space.h"
endpoint::endpoint() : endpoint::endpoint() :
@@ -25,16 +24,13 @@ endpoint::close()
j6_status_t j6_status_t
endpoint::send(size_t len, void *data) endpoint::send(size_t len, void *data)
{ {
scheduler &s = scheduler::get(); thread_data sender = { &thread::current(), data };
TCB *tcb = s.current();
thread_data sender = { thread::from_tcb(tcb), data };
sender.len = len; sender.len = len;
if (!check_signal(j6_signal_endpoint_can_send)) { if (!check_signal(j6_signal_endpoint_can_send)) {
assert_signal(j6_signal_endpoint_can_recv); assert_signal(j6_signal_endpoint_can_recv);
sender.th->wait_on_object(this);
m_blocked.append(sender); m_blocked.append(sender);
s.schedule(); sender.th->wait_on_object(this);
// we woke up having already finished the send // we woke up having already finished the send
// because it happened in the receiver // because it happened in the receiver
@@ -54,16 +50,13 @@ endpoint::send(size_t len, void *data)
j6_status_t j6_status_t
endpoint::receive(size_t *len, void *data) endpoint::receive(size_t *len, void *data)
{ {
scheduler &s = scheduler::get(); thread_data receiver = { &thread::current(), data };
TCB *tcb = s.current();
thread_data receiver = { thread::from_tcb(tcb), data };
receiver.len_p = len; receiver.len_p = len;
if (!check_signal(j6_signal_endpoint_can_recv)) { if (!check_signal(j6_signal_endpoint_can_recv)) {
assert_signal(j6_signal_endpoint_can_send); assert_signal(j6_signal_endpoint_can_send);
receiver.th->wait_on_object(this);
m_blocked.append(receiver); m_blocked.append(receiver);
s.schedule(); receiver.th->wait_on_object(this);
// we woke up having already finished the recv // we woke up having already finished the recv
// because it happened in the sender // because it happened in the sender

View File

@@ -5,6 +5,7 @@
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "scheduler.h"
// This object is initialized _before_ global constructors are called, // This object is initialized _before_ global constructors are called,
// so we don't want it to have a global constructor at all, lest it // so we don't want it to have a global constructor at all, lest it
@@ -62,6 +63,9 @@ process::exit(unsigned code)
} }
m_return_code = code; m_return_code = code;
assert_signal(j6_signal_process_exit); assert_signal(j6_signal_process_exit);
if (this == bsp_cpu_data.p)
scheduler::get().schedule();
} }
void void
@@ -90,6 +94,9 @@ process::update()
thread * thread *
process::create_thread(uint8_t priority, bool user) process::create_thread(uint8_t priority, bool user)
{ {
if (priority == default_pri)
priority = scheduler::default_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!");
@@ -104,6 +111,7 @@ process::create_thread(uint8_t priority, bool user)
} }
m_threads.append(th); m_threads.append(th);
scheduler::get().add_thread(th->tcb());
return th; return th;
} }

View File

@@ -18,6 +18,9 @@ public:
/// Size of userspace thread stacks /// Size of userspace thread stacks
constexpr static size_t stack_size = 0x4000; constexpr static size_t stack_size = 0x4000;
/// Value that represents default priority
constexpr static uint8_t default_pri = 0xff;
/// Constructor. /// Constructor.
process(); process();
@@ -43,7 +46,7 @@ public:
/// \args priority The new thread's scheduling priority /// \args priority The new thread's scheduling priority
/// \args user If true, create a userspace stack for this thread /// \args user If true, create a userspace stack for this thread
/// \returns The newly created thread object /// \returns The newly created thread object
thread * create_thread(uint8_t priorty, bool user = true); thread * create_thread(uint8_t priorty = default_pri, bool user = true);
/// Start tracking an object with a handle. /// Start tracking an object with a handle.
/// \args obj The object this handle refers to /// \args obj The object this handle refers to

View File

@@ -26,8 +26,6 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
setup_kernel_stack(); setup_kernel_stack();
else else
m_tcb.rsp0 = rsp0; m_tcb.rsp0 = rsp0;
set_state(state::ready);
} }
thread::~thread() thread::~thread()
@@ -49,12 +47,16 @@ thread::current()
return *bsp_cpu_data.t; return *bsp_cpu_data.t;
} }
inline void schedule_if_current(thread *t) { if (t == bsp_cpu_data.t) scheduler::get().schedule(); }
void void
thread::wait_on_signals(kobject *obj, j6_signal_t signals) thread::wait_on_signals(kobject *obj, j6_signal_t signals)
{ {
m_wait_type = wait_type::signal; m_wait_type = wait_type::signal;
m_wait_data = signals; m_wait_data = signals;
clear_state(state::ready); clear_state(state::ready);
schedule_if_current(this);
} }
void void
@@ -63,6 +65,8 @@ thread::wait_on_time(uint64_t t)
m_wait_type = wait_type::time; m_wait_type = wait_type::time;
m_wait_data = t; m_wait_data = t;
clear_state(state::ready); clear_state(state::ready);
schedule_if_current(this);
} }
void void
@@ -71,6 +75,8 @@ thread::wait_on_object(kobject *o)
m_wait_type = wait_type::object; m_wait_type = wait_type::object;
m_wait_data = reinterpret_cast<uint64_t>(o); m_wait_data = reinterpret_cast<uint64_t>(o);
clear_state(state::ready); clear_state(state::ready);
schedule_if_current(this);
} }
bool bool
@@ -134,6 +140,8 @@ thread::exit(uint32_t code)
set_state(state::exited); set_state(state::exited);
clear_state(state::ready); clear_state(state::ready);
assert_signal(j6_signal_thread_exit); assert_signal(j6_signal_thread_exit);
schedule_if_current(this);
} }
void void
@@ -200,7 +208,8 @@ thread *
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0) thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
{ {
thread *idle = new thread(kernel, pri, rsp0); thread *idle = new thread(kernel, pri, rsp0);
idle->set_state(thread::state::constant); idle->set_state(state::constant);
idle->set_state(state::ready);
log::info(logs::task, "Created idle thread as koid %llx", idle->koid()); log::info(logs::task, "Created idle thread as koid %llx", idle->koid());
return idle; return idle;

View File

@@ -141,13 +141,14 @@ scheduler::create_process(bool user)
{ {
process *p = new process; process *p = new process;
thread *th = p->create_thread(default_priority, user); thread *th = p->create_thread(default_priority, user);
auto *tcb = th->tcb();
auto *tcb = th->tcb();
tcb->time_left = quantum(default_priority); tcb->time_left = quantum(default_priority);
log::debug(logs::task, "Creating thread %llx, priority %d, time slice %d", log::debug(logs::task, "Creating thread %llx, priority %d, time slice %d",
th->koid(), tcb->priority, tcb->time_left); th->koid(), tcb->priority, tcb->time_left);
th->set_state(thread::state::ready);
return th; return th;
} }
@@ -183,8 +184,6 @@ scheduler::load_process(const char *name, const void *data, size_t size)
tcb->rsp3 = process::stacks_top; tcb->rsp3 = process::stacks_top;
m_runlists[default_priority].push_back(tcb);
log::debug(logs::task, "Loading thread %s: koid %llx pri %d", name, th->koid(), tcb->priority); log::debug(logs::task, "Loading thread %s: koid %llx pri %d", name, th->koid(), tcb->priority);
log::debug(logs::task, " RSP %016lx", tcb->rsp); log::debug(logs::task, " RSP %016lx", tcb->rsp);
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0); log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
@@ -203,7 +202,7 @@ scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
if (constant) if (constant)
th->set_state(thread::state::constant); th->set_state(thread::state::constant);
m_runlists[priority].push_back(tcb); th->set_state(thread::state::ready);
log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority); log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority);
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0); log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
@@ -247,7 +246,6 @@ void scheduler::prune(uint64_t now)
if (!exited && !ready) if (!exited && !ready)
continue; continue;
if (exited) { if (exited) {
// If the current thread has exited, wait until the next call // If the current thread has exited, wait until the next call
// to prune() to delete it, because we may be deleting our current // to prune() to delete it, because we may be deleting our current

View File

@@ -69,7 +69,7 @@ public:
/// \returns A pointer to the current thread's TCB /// \returns A pointer to the current thread's TCB
inline TCB * current() { return m_current; } inline TCB * current() { return m_current; }
inline void add_thread(TCB *t) { m_runlists[t->priority].push_back(static_cast<tcb_node*>(t)); } inline void add_thread(TCB *t) { m_blocked.push_back(static_cast<tcb_node*>(t)); }
/// Get a reference to the system scheduler /// Get a reference to the system scheduler
/// \returns A reference to the global system scheduler /// \returns A reference to the global system scheduler

View File

@@ -5,14 +5,12 @@
#include "log.h" #include "log.h"
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "scheduler.h"
namespace syscalls { namespace syscalls {
j6_status_t j6_status_t
object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs) object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
{ {
scheduler &s = scheduler::get();
thread &th = thread::current(); thread &th = thread::current();
process &p = process::current(); process &p = process::current();
@@ -28,7 +26,6 @@ object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
obj->add_blocked_thread(&th); obj->add_blocked_thread(&th);
th.wait_on_signals(obj, mask); th.wait_on_signals(obj, mask);
s.schedule();
j6_status_t result = th.get_wait_result(); j6_status_t result = th.get_wait_result();
if (result == j6_status_ok) { if (result == j6_status_ok) {

View File

@@ -3,7 +3,6 @@
#include "log.h" #include "log.h"
#include "objects/process.h" #include "objects/process.h"
#include "scheduler.h"
namespace syscalls { namespace syscalls {
@@ -20,13 +19,10 @@ process_koid(j6_koid_t *koid)
j6_status_t j6_status_t
process_exit(int64_t status) process_exit(int64_t status)
{ {
auto &s = scheduler::get();
process &p = process::current(); process &p = process::current();
log::debug(logs::syscall, "Process %llx exiting with code %d", p.koid(), status); log::debug(logs::syscall, "Process %llx exiting with code %d", p.koid(), status);
p.exit(status); p.exit(status);
s.schedule();
log::error(logs::syscall, "returned to exit syscall"); log::error(logs::syscall, "returned to exit syscall");
return j6_err_unexpected; return j6_err_unexpected;

View File

@@ -4,7 +4,6 @@
#include "log.h" #include "log.h"
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "scheduler.h"
namespace syscalls { namespace syscalls {
@@ -21,14 +20,14 @@ thread_koid(j6_koid_t *koid)
j6_status_t j6_status_t
thread_create(void *rip, j6_handle_t *handle) thread_create(void *rip, j6_handle_t *handle)
{ {
scheduler &s = scheduler::get();
thread &parent = thread::current(); thread &parent = thread::current();
process &p = parent.parent(); process &p = parent.parent();
thread *child = p.create_thread(scheduler::default_priority); thread *child = p.create_thread();
child->add_thunk_user(reinterpret_cast<uintptr_t>(rip)); child->add_thunk_user(reinterpret_cast<uintptr_t>(rip));
*handle = p.add_handle(child); *handle = p.add_handle(child);
s.add_thread(child->tcb()); child->clear_state(thread::state::loading);
child->set_state(thread::state::ready);
log::debug(logs::syscall, "Thread %llx spawned new thread %llx, handle %d", log::debug(logs::syscall, "Thread %llx spawned new thread %llx, handle %d",
parent.koid(), child->koid(), *handle); parent.koid(), child->koid(), *handle);
@@ -39,11 +38,9 @@ thread_create(void *rip, j6_handle_t *handle)
j6_status_t j6_status_t
thread_exit(int64_t status) thread_exit(int64_t status)
{ {
auto &s = scheduler::get();
thread &th = thread::current(); thread &th = thread::current();
log::debug(logs::syscall, "Thread %llx exiting with code %d", th.koid(), status); log::debug(logs::syscall, "Thread %llx exiting with code %d", th.koid(), status);
th.exit(status); th.exit(status);
s.schedule();
log::error(logs::syscall, "returned to exit syscall"); log::error(logs::syscall, "returned to exit syscall");
return j6_err_unexpected; return j6_err_unexpected;
@@ -52,22 +49,18 @@ thread_exit(int64_t status)
j6_status_t j6_status_t
thread_pause() thread_pause()
{ {
auto &s = scheduler::get();
thread &th = thread::current(); thread &th = thread::current();
th.wait_on_signals(&th, -1ull); th.wait_on_signals(&th, -1ull);
s.schedule();
return j6_status_ok; return j6_status_ok;
} }
j6_status_t j6_status_t
thread_sleep(uint64_t til) thread_sleep(uint64_t til)
{ {
auto &s = scheduler::get();
thread &th = thread::current(); thread &th = thread::current();
log::debug(logs::syscall, "Thread %llx sleeping until %llu", th.koid(), til); log::debug(logs::syscall, "Thread %llx sleeping until %llu", th.koid(), til);
th.wait_on_time(til); th.wait_on_time(til);
s.schedule();
return j6_status_ok; return j6_status_ok;
} }