From c1618e2b5d019cbe1e05ea950fd1e8a9a5b92a6b Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 1 Apr 2025 23:08:27 +0300 Subject: [PATCH] Kernel/LibC: Add basic support for pthread_{create,exit} --- kernel/include/kernel/Process.h | 10 ++++ kernel/include/kernel/Thread.h | 2 + kernel/kernel/Process.cpp | 42 ++++++++++++++++ kernel/kernel/Thread.cpp | 46 +++++++++++++---- userspace/libraries/LibC/CMakeLists.txt | 1 + .../libraries/LibC/include/sys/syscall.h | 2 + userspace/libraries/LibC/pthread.cpp | 49 +++++++++++++++++++ userspace/tests/test-pthread/CMakeLists.txt | 8 +++ userspace/tests/test-pthread/main.cpp | 28 +++++++++++ 9 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 userspace/libraries/LibC/pthread.cpp create mode 100644 userspace/tests/test-pthread/CMakeLists.txt create mode 100644 userspace/tests/test-pthread/main.cpp diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 0ba814cb..29c1d9e4 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -182,6 +182,9 @@ namespace Kernel BAN::ErrorOr sys_sigpending(sigset_t* set); BAN::ErrorOr sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset); + BAN::ErrorOr sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg); + BAN::ErrorOr sys_pthread_exit(void* value); + BAN::ErrorOr sys_tcgetpgrp(int fd); BAN::ErrorOr sys_tcsetpgrp(int fd, pid_t pgid); @@ -290,6 +293,13 @@ namespace Kernel BAN::Vector m_threads; + struct pthread_info_t + { + Thread* thread; + void* value; + }; + BAN::Vector m_exited_pthreads; + uint64_t m_alarm_interval_ns { 0 }; uint64_t m_alarm_wake_time_ns { 0 }; diff --git a/kernel/include/kernel/Thread.h b/kernel/include/kernel/Thread.h index 35d05f54..a861a2bc 100644 --- a/kernel/include/kernel/Thread.h +++ b/kernel/include/kernel/Thread.h @@ -38,6 +38,8 @@ namespace Kernel static BAN::ErrorOr create_userspace(Process*, PageTable&); ~Thread(); + BAN::ErrorOr pthread_create(entry_t, void*); + BAN::ErrorOr clone(Process*, uintptr_t sp, uintptr_t ip); void setup_exec(); void setup_process_cleanup(); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index f6ac31b0..1f6c05b6 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -185,6 +185,8 @@ namespace Kernel process->m_userspace_info.envp = nullptr; auto* thread = MUST(Thread::create_userspace(process, process->page_table())); + thread->setup_exec(); + process->add_thread(thread); process->register_to_scheduler(); return process; @@ -208,6 +210,7 @@ namespace Kernel Process::~Process() { ASSERT(m_threads.empty()); + ASSERT(m_exited_pthreads.empty()); ASSERT(m_mapped_regions.empty()); ASSERT(!m_page_table); } @@ -238,6 +241,8 @@ namespace Kernel } } + m_exited_pthreads.clear(); + ProcFileSystem::get().on_process_delete(*this); m_process_lock.lock(); @@ -2079,6 +2084,43 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg) + { + if (attr != nullptr) + { + dwarnln("pthread attr not supported"); + return BAN::Error::from_errno(ENOTSUP); + } + + LockGuard _(m_process_lock); + + auto* new_thread = TRY(Thread::current().pthread_create(entry, arg)); + MUST(m_threads.push_back(new_thread)); + MUST(Processor::scheduler().add_thread(new_thread)); + + return new_thread->tid(); + } + + BAN::ErrorOr Process::sys_pthread_exit(void* value) + { + LockGuard _(m_process_lock); + + // main thread cannot call pthread_exit + if (&Thread::current() == m_threads.front()) + return BAN::Error::from_errno(EINVAL); + + TRY(m_exited_pthreads.emplace_back(&Thread::current(), value)); + for (auto* thread : m_threads) + { + if (thread != &Thread::current()) + continue; + m_process_lock.unlock(); + thread->on_exit(); + } + + ASSERT_NOT_REACHED(); + } + BAN::ErrorOr Process::sys_tcgetpgrp(int fd) { LockGuard _(m_process_lock); diff --git a/kernel/kernel/Thread.cpp b/kernel/kernel/Thread.cpp index 38ed8a93..c3fa38a9 100644 --- a/kernel/kernel/Thread.cpp +++ b/kernel/kernel/Thread.cpp @@ -117,8 +117,6 @@ namespace Kernel true )); - thread->setup_exec(); - thread_deleter.disable(); return thread; @@ -166,6 +164,21 @@ namespace Kernel } } + BAN::ErrorOr Thread::pthread_create(entry_t entry, void* arg) + { + auto* thread = TRY(create_userspace(m_process, m_process->page_table())); + + memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage)); + + thread->setup_exec_impl( + reinterpret_cast(entry), + reinterpret_cast(arg), + 0, 0, 0 + ); + + return thread; + } + BAN::ErrorOr Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip) { ASSERT(m_is_userspace); @@ -189,6 +202,8 @@ namespace Kernel thread->m_interrupt_stack.sp = sp; thread->m_interrupt_stack.ss = 0x10; + memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage)); + #if ARCH(x86_64) thread->m_interrupt_registers.rax = 0; #elif ARCH(i686) @@ -201,23 +216,34 @@ namespace Kernel } void Thread::setup_exec() + { + const auto& userspace_info = process().userspace_info(); + ASSERT(userspace_info.entry); + + setup_exec_impl( + userspace_info.entry, + userspace_info.argc, + reinterpret_cast(userspace_info.argv), + reinterpret_cast(userspace_info.envp), + userspace_info.file_fd + ); + } + + void Thread::setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) { ASSERT(is_userspace()); m_state = State::NotStarted; // Signal mask is inherited - const auto& userspace_info = process().userspace_info(); - ASSERT(userspace_info.entry); - // Initialize stack for returning PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] { uintptr_t sp = PageTable::fast_page() + PAGE_SIZE; - write_to_stack(sp, userspace_info.entry); - write_to_stack(sp, userspace_info.file_fd); - write_to_stack(sp, userspace_info.envp); - write_to_stack(sp, userspace_info.argv); - write_to_stack(sp, userspace_info.argc); + write_to_stack(sp, entry); + write_to_stack(sp, arg3); + write_to_stack(sp, arg2); + write_to_stack(sp, arg1); + write_to_stack(sp, arg0); }); m_interrupt_stack.ip = reinterpret_cast(start_userspace_thread); diff --git a/userspace/libraries/LibC/CMakeLists.txt b/userspace/libraries/LibC/CMakeLists.txt index e4afbc77..abb54937 100644 --- a/userspace/libraries/LibC/CMakeLists.txt +++ b/userspace/libraries/LibC/CMakeLists.txt @@ -17,6 +17,7 @@ set(LIBC_SOURCES netdb.cpp poll.cpp printf_impl.cpp + pthread.cpp pwd.cpp scanf_impl.cpp setjmp.cpp diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index e8a2bb2e..9e0c0529 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -90,6 +90,8 @@ __BEGIN_DECLS O(SYS_FSYNC, fsync) \ O(SYS_SYMLINKAT, symlinkat) \ O(SYS_HARDLINKAT, hardlinkat) \ + O(SYS_PTHREAD_CREATE, pthread_create) \ + O(SYS_PTHREAD_EXIT, pthread_exit) \ enum Syscall { diff --git a/userspace/libraries/LibC/pthread.cpp b/userspace/libraries/LibC/pthread.cpp new file mode 100644 index 00000000..80399312 --- /dev/null +++ b/userspace/libraries/LibC/pthread.cpp @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include +#include + +struct pthread_trampoline_info_t +{ + void* (*start_routine)(void*); + void* arg; +}; + +static void pthread_trampoline(void* arg) +{ + pthread_trampoline_info_t info; + memcpy(&info, arg, sizeof(pthread_trampoline_info_t)); + free(arg); + + pthread_exit(info.start_routine(info.arg)); + ASSERT_NOT_REACHED(); +} + +int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg) +{ + auto* info = static_cast(malloc(sizeof(pthread_trampoline_info_t))); + if (info == nullptr) + return -1; + info->start_routine = start_routine; + info->arg = arg; + + const auto ret = syscall(SYS_PTHREAD_CREATE, attr, pthread_trampoline, info); + if (ret == -1) + { + free(info); + return -1; + } + + if (thread) + *thread = ret; + return 0; +} + +void pthread_exit(void* value_ptr) +{ + syscall(SYS_PTHREAD_EXIT, value_ptr); + ASSERT_NOT_REACHED(); +} diff --git a/userspace/tests/test-pthread/CMakeLists.txt b/userspace/tests/test-pthread/CMakeLists.txt new file mode 100644 index 00000000..59880e4d --- /dev/null +++ b/userspace/tests/test-pthread/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SOURCES + main.cpp +) + +add_executable(test-pthread ${SOURCES}) +banan_link_library(test-pthread libc) + +install(TARGETS test-pthread OPTIONAL) diff --git a/userspace/tests/test-pthread/main.cpp b/userspace/tests/test-pthread/main.cpp new file mode 100644 index 00000000..53562070 --- /dev/null +++ b/userspace/tests/test-pthread/main.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +void* thread_func(void*) +{ + printf("hello from thread\n"); + return nullptr; +} + +int main(int argc, char** argv) +{ + pthread_t tid; + + printf("creating thread\n"); + + if (pthread_create(&tid, nullptr, &thread_func, nullptr) == -1) + { + perror("pthread_create"); + return 1; + } + + sleep(1); + + printf("exiting\n"); + + return 0; +}