Kernel: Improve multithreading support
We can now use arbitary BAN::function<void(...)> as the Thread. I also implemented multithreading for i386 since it was not done on the initial multithreading commit.
This commit is contained in:
@@ -7,18 +7,9 @@ namespace Kernel
|
||||
|
||||
static Scheduler* s_instance = nullptr;
|
||||
|
||||
extern "C" void start_thread(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t rsp, uintptr_t rbp, uintptr_t rip);
|
||||
extern "C" void continue_thread(uintptr_t rsp, uintptr_t rbp, uintptr_t rip);
|
||||
extern "C" uintptr_t read_rip();
|
||||
asm(
|
||||
".global read_rip;"
|
||||
"read_rip:"
|
||||
#if ARCH(x86_64)
|
||||
"popq %rax;"
|
||||
"jmp *%rax"
|
||||
#else
|
||||
"popl %eax;"
|
||||
"jmp *%eax"
|
||||
#endif
|
||||
);
|
||||
|
||||
void Scheduler::initialize()
|
||||
{
|
||||
@@ -37,13 +28,14 @@ namespace Kernel
|
||||
return *m_current_iterator;
|
||||
}
|
||||
|
||||
//void Scheduler::AddThread(const BAN::Function<void()>& function)
|
||||
//{
|
||||
// MUST(m_threads.EmplaceBack(function));
|
||||
//}
|
||||
|
||||
void Scheduler::switch_thread()
|
||||
{
|
||||
uintptr_t rsp, rbp, rip;
|
||||
if (!(rip = read_rip()))
|
||||
return;
|
||||
read_rsp(rsp);
|
||||
read_rbp(rbp);
|
||||
|
||||
static uint8_t cnt = 0;
|
||||
if (cnt++ % ms_between_switch)
|
||||
return;
|
||||
@@ -63,6 +55,8 @@ namespace Kernel
|
||||
Thread& current = *m_current_iterator;
|
||||
Thread& next = *next_iterator;
|
||||
|
||||
ASSERT(next.state() == Thread::State::Paused || next.state() == Thread::State::NotStarted);
|
||||
|
||||
if (current.state() == Thread::State::Done)
|
||||
{
|
||||
// NOTE: this does not invalidate the next/next_iterator
|
||||
@@ -71,21 +65,11 @@ namespace Kernel
|
||||
m_current_iterator = decltype(m_threads)::iterator();
|
||||
}
|
||||
|
||||
uintptr_t rip = read_rip();
|
||||
if (rip == 0)
|
||||
return;
|
||||
|
||||
uintptr_t rsp;
|
||||
#if ARCH(x86_64)
|
||||
asm volatile("movq %%rsp, %0" : "=r"(rsp));
|
||||
#else
|
||||
asm volatile("movl %%esp, %0" : "=r"(rsp));
|
||||
#endif
|
||||
|
||||
if (m_current_iterator)
|
||||
{
|
||||
current.set_rip(rip);
|
||||
current.set_rsp(rsp);
|
||||
current.set_rbp(rbp);
|
||||
current.set_rip(rip);
|
||||
current.set_state(Thread::State::Paused);
|
||||
}
|
||||
|
||||
@@ -93,33 +77,16 @@ namespace Kernel
|
||||
|
||||
if (next.state() == Thread::State::NotStarted)
|
||||
{
|
||||
InterruptController::Get().EOI(PIT_IRQ);
|
||||
InterruptController::get().eoi(PIT_IRQ);
|
||||
next.set_state(Thread::State::Running);
|
||||
asm volatile(
|
||||
#if ARCH(x86_64)
|
||||
"movq %0, %%rsp;"
|
||||
#else
|
||||
"movl %0, %%esp;"
|
||||
#endif
|
||||
"sti;"
|
||||
"jmp *%1;"
|
||||
:: "r"(next.rsp()), "r"(next.rip())
|
||||
);
|
||||
const uintptr_t* args = next.args();
|
||||
start_thread(args[0], args[1], args[2], args[3], next.rsp(), next.rbp(), next.rip());
|
||||
}
|
||||
else if (next.state() == Thread::State::Paused)
|
||||
{
|
||||
next.set_state(Thread::State::Running);
|
||||
asm volatile(
|
||||
#if ARCH(x86_64)
|
||||
"movq %0, %%rsp;"
|
||||
"movq $0, %%rax;"
|
||||
#else
|
||||
"movl %0, %%esp;"
|
||||
"movl $0, %%eax;"
|
||||
#endif
|
||||
"jmp *%1;"
|
||||
:: "r"(next.rsp()), "r"(next.rip())
|
||||
);
|
||||
BOCHS_BREAK();
|
||||
continue_thread(next.rsp(), next.rbp(), next.rip());
|
||||
}
|
||||
|
||||
ASSERT(false);
|
||||
@@ -134,16 +101,11 @@ namespace Kernel
|
||||
Thread& current = *m_current_iterator;
|
||||
ASSERT(current.state() == Thread::State::NotStarted);
|
||||
current.set_state(Thread::State::Running);
|
||||
asm volatile(
|
||||
#if ARCH(x86_64)
|
||||
"movq %0, %%rsp;"
|
||||
#else
|
||||
"movl %0, %%esp;"
|
||||
#endif
|
||||
"sti;"
|
||||
"jmp *%1;"
|
||||
:: "r"(current.rsp()), "r"(current.rip())
|
||||
);
|
||||
|
||||
const uintptr_t* args = current.args();
|
||||
start_thread(args[0], args[1], args[2], args[3], current.rsp(), current.rbp(), current.rip());
|
||||
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,23 +14,37 @@ namespace Kernel
|
||||
|
||||
static constexpr size_t thread_stack_size = PAGE_SIZE;
|
||||
|
||||
template<typename T>
|
||||
template<size_t size, typename T>
|
||||
static void write_to_stack(uintptr_t& rsp, const T& value)
|
||||
{
|
||||
rsp -= sizeof(T);
|
||||
*(T*)rsp = value;
|
||||
rsp -= size;
|
||||
memcpy((void*)rsp, (void*)&value, size);
|
||||
}
|
||||
|
||||
Thread::Thread(void(*function)())
|
||||
Thread::Thread(uintptr_t rip, uintptr_t func, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
|
||||
: m_id(s_next_id++)
|
||||
{
|
||||
m_stack_base = kmalloc(thread_stack_size, PAGE_SIZE);
|
||||
ASSERT(m_stack_base);
|
||||
|
||||
m_rbp = (uintptr_t)m_stack_base + thread_stack_size;
|
||||
m_rsp = m_rbp;
|
||||
m_rip = rip;
|
||||
m_args[1] = arg1;
|
||||
m_args[2] = arg2;
|
||||
m_args[3] = arg3;
|
||||
|
||||
m_rip = (uintptr_t)function;
|
||||
m_rsp = (uintptr_t)m_stack_base + thread_stack_size;
|
||||
write_to_stack(m_rsp, this);
|
||||
write_to_stack(m_rsp, &Thread::on_exit);
|
||||
// NOTE: in System V ABI arg0 is the pointer to 'this'
|
||||
// we copy the function object to Thread object
|
||||
// so we can ensure the lifetime of it. We store
|
||||
// it as raw bytes so that Thread can be non-templated.
|
||||
// This requires BAN::Function to be trivially copyable
|
||||
// but for now it should be.
|
||||
memcpy(m_function, (void*)func, sizeof(m_function));
|
||||
m_args[0] = (uintptr_t)m_function;
|
||||
|
||||
write_to_stack<sizeof(void*)>(m_rsp, this);
|
||||
write_to_stack<sizeof(void*)>(m_rsp, &Thread::on_exit);
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
@@ -40,13 +54,7 @@ namespace Kernel
|
||||
|
||||
void Thread::on_exit()
|
||||
{
|
||||
Thread* thread = nullptr;
|
||||
#if ARCH(x86_64)
|
||||
asm volatile("movq (%%rsp), %0" : "=r"(thread));
|
||||
#else
|
||||
asm volatile("movl (%%esp), %0" : "=r"(thread));
|
||||
#endif
|
||||
thread->m_state = State::Done;
|
||||
m_state = State::Done;
|
||||
for (;;) asm volatile("hlt");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user