diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 1b23a290..18900705 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -185,6 +185,7 @@ namespace Kernel BAN::ErrorOr sys_yield(); 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_pthread_join(pthread_t thread, void** value); BAN::ErrorOr sys_pthread_self(); BAN::ErrorOr sys_tcgetpgrp(int fd); @@ -297,10 +298,11 @@ namespace Kernel struct pthread_info_t { - Thread* thread; + pthread_t thread; void* value; }; BAN::Vector m_exited_pthreads; + ThreadBlocker m_pthread_exit_blocker; uint64_t m_alarm_interval_ns { 0 }; uint64_t m_alarm_wake_time_ns { 0 }; diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 4f9b1507..302322d7 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -2115,11 +2115,12 @@ namespace Kernel if (&Thread::current() == m_threads.front()) return BAN::Error::from_errno(EINVAL); - TRY(m_exited_pthreads.emplace_back(&Thread::current(), value)); + TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value)); for (auto* thread : m_threads) { if (thread != &Thread::current()) continue; + m_pthread_exit_blocker.unblock(); m_process_lock.unlock(); thread->on_exit(); } @@ -2127,6 +2128,58 @@ namespace Kernel ASSERT_NOT_REACHED(); } + BAN::ErrorOr Process::sys_pthread_join(pthread_t thread, void** value) + { + LockGuard _(m_process_lock); + + if (value) + TRY(validate_pointer_access(value, sizeof(void*), true)); + + if (thread == Thread::current().tid()) + return BAN::Error::from_errno(EINVAL); + + const auto wait_thread = + [&]() -> bool + { + for (size_t i = 0; i < m_exited_pthreads.size(); i++) + { + if (m_exited_pthreads[i].thread != thread) + continue; + + if (value) + *value = m_exited_pthreads[i].value; + m_exited_pthreads.remove(i); + + return true; + } + + return false; + }; + + if (wait_thread()) + return 0; + + { + bool found = false; + for (auto* _thread : m_threads) + if (_thread->tid() == thread) + found = true; + if (!found) + return BAN::Error::from_errno(EINVAL); + } + + for (;;) + { + { + LockFreeGuard _(m_process_lock); + m_pthread_exit_blocker.block_with_timeout_ms(100); + } + + if (wait_thread()) + return 0; + } + } + BAN::ErrorOr Process::sys_pthread_self() { return Thread::current().tid(); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 32de461b..273fc7c5 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -93,6 +93,7 @@ __BEGIN_DECLS O(SYS_YIELD, yield) \ O(SYS_PTHREAD_CREATE, pthread_create) \ O(SYS_PTHREAD_EXIT, pthread_exit) \ + O(SYS_PTHREAD_JOIN, pthread_join) \ O(SYS_PTHREAD_SELF, pthread_self) \ enum Syscall diff --git a/userspace/libraries/LibC/pthread.cpp b/userspace/libraries/LibC/pthread.cpp index ab75c899..d041b7da 100644 --- a/userspace/libraries/LibC/pthread.cpp +++ b/userspace/libraries/LibC/pthread.cpp @@ -55,6 +55,11 @@ void pthread_exit(void* value_ptr) ASSERT_NOT_REACHED(); } +int pthread_join(pthread_t thread, void** value_ptr) +{ + return syscall(SYS_PTHREAD_JOIN, thread, value_ptr); +} + pthread_t pthread_self(void) { return syscall(SYS_PTHREAD_SELF);