[libj6] Make j6::thread a template for lambdas

Instead of a C-style function pointer taking `void *userdata`, let
j6::thread take a lambda as its thread procedure.
This commit is contained in:
Justin C. Miller
2023-08-09 21:07:09 -07:00
parent 8b3fa3ed01
commit 3cfd0cf86b
4 changed files with 47 additions and 78 deletions

View File

@@ -8,40 +8,75 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <j6/flags.h>
#include <j6/memutils.h>
#include <j6/types.h> #include <j6/types.h>
#include <j6/syscalls.h>
namespace j6 { namespace j6 {
template <typename Proc>
class thread class thread
{ {
public: public:
using proc = void (*)(void *);
/// Constructor. Create a thread and its stack space, but /// Constructor. Create a thread and its stack space, but
/// do not start executing the thread. /// do not start executing the thread.
/// \arg p The function where the thread will begin execution /// \arg p The function where the thread will begin execution
/// \arg stack_top The address where the top of this thread's stack should be mapped /// \arg stack_top The address where the top of this thread's stack should be mapped
thread(proc p, uintptr_t stack_top); /// \arg stack_size Size of the stack, in bytes (default 64KiB)
thread(Proc p, uintptr_t stack_top, size_t stack_size = 0x10000) :
m_stack {j6_handle_invalid},
m_thread {j6_handle_invalid},
m_stack_top {stack_top},
m_proc {p}
{
uintptr_t stack_base = stack_top - stack_size;
m_status = j6_vma_create_map(&m_stack, stack_size, stack_base, j6_vm_flag_write);
if (m_status != j6_status_ok)
return;
static constexpr size_t zeros_size = 0x10;
m_stack_top -= zeros_size; // Sentinel
memset(reinterpret_cast<void*>(m_stack_top), 0, zeros_size);
}
/// Start executing the thread. /// Start executing the thread.
/// \arg user Optional pointer to user data to pass to the thread proc
/// \returns j6_status_ok if the thread was successfully started. /// \returns j6_status_ok if the thread was successfully started.
j6_status_t start(void *user = nullptr); j6_status_t start()
{
if (m_status != j6_status_ok)
return m_status;
if (m_thread != j6_handle_invalid)
return j6_err_invalid_arg;
uint64_t arg0 = reinterpret_cast<uint64_t>(this);
m_status = j6_thread_create(&m_thread, 0,
m_stack_top, reinterpret_cast<uintptr_t>(init_proc),
arg0, 0);
return m_status;
}
/// Wait for the thread to stop executing. /// Wait for the thread to stop executing.
void join(); void join() { j6_thread_join(m_thread); }
thread() = delete; thread() = delete;
thread(const thread&) = delete; thread(const thread&) = delete;
private: private:
static void init_proc(thread *t, void *user); static void init_proc(thread *t)
{
t->m_proc();
j6_thread_exit();
}
j6_status_t m_status; j6_status_t m_status;
j6_handle_t m_stack; j6_handle_t m_stack;
j6_handle_t m_thread; j6_handle_t m_thread;
uintptr_t m_stack_top; uintptr_t m_stack_top;
proc m_proc; Proc m_proc;
}; };
} // namespace j6 } // namespace j6

View File

@@ -15,7 +15,6 @@ j6 = module("j6",
"syscalls.s.cog", "syscalls.s.cog",
"sysconf.cpp.cog", "sysconf.cpp.cog",
"syslog.cpp", "syslog.cpp",
"thread.cpp",
], ],
public_headers = [ public_headers = [
"j6/cap_flags.h.cog", "j6/cap_flags.h.cog",

View File

@@ -1,65 +0,0 @@
// The kernel depends on libj6 for some shared code,
// but should not include the user-specific code.
#ifndef __j6kernel
#include <j6/errors.h>
#include <j6/flags.h>
#include <j6/memutils.h>
#include <j6/syscalls.h>
#include <j6/thread.hh>
namespace j6 {
static constexpr size_t stack_size = 0x10000;
static constexpr size_t zeros_size = 0x10;
thread::thread(thread::proc p, uintptr_t stack_top) :
m_stack {j6_handle_invalid},
m_thread {j6_handle_invalid},
m_stack_top {stack_top},
m_proc {p}
{
uintptr_t stack_base = stack_top - stack_size;
m_status = j6_vma_create_map(&m_stack, stack_size, stack_base, j6_vm_flag_write);
if (m_status != j6_status_ok)
return;
m_stack_top -= zeros_size;
memset(reinterpret_cast<void*>(m_stack_top), 0, zeros_size);
}
j6_status_t
thread::start(void *user)
{
if (m_status != j6_status_ok)
return m_status;
if (m_thread != j6_handle_invalid)
return j6_err_invalid_arg;
uint64_t arg0 = reinterpret_cast<uint64_t>(this);
uint64_t arg1 = reinterpret_cast<uint64_t>(user);
m_status = j6_thread_create(&m_thread, 0,
m_stack_top, reinterpret_cast<uintptr_t>(init_proc),
arg0, arg1);
return m_status;
}
void
thread::join()
{
j6_thread_join(m_thread);
}
void
thread::init_proc(thread *t, void *user)
{
t->m_proc(user);
j6_thread_exit();
}
} // namespace j6
#endif // __j6kernel

View File

@@ -111,8 +111,8 @@ main(int argc, const char **argv)
j6_log("main thread created channel"); j6_log("main thread created channel");
j6::thread child_thread {thread_proc, stack_top}; j6::thread child_thread {[=](){ thread_proc(chan); }, stack_top};
j6_status_t result = child_thread.start(chan); j6_status_t result = child_thread.start();
if (result != j6_status_ok) if (result != j6_status_ok)
return result; return result;