diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index d9f942c480..d1f3ab495b 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2862,9 +2862,9 @@ int Process::sys$create_thread(void* (*entry)(void*), void* argument) return thread->tid(); } -void Process::sys$exit_thread(void* code) +void Process::sys$exit_thread(void* exit_value) { - UNUSED_PARAM(code); + current->m_exit_value = exit_value; cli(); if (¤t->process().main_thread() == current) { // FIXME: For POSIXy reasons, we should only sys$exit once *all* threads have exited. @@ -2877,6 +2877,47 @@ void Process::sys$exit_thread(void* code) ASSERT_NOT_REACHED(); } +int Process::sys$join_thread(int tid, void** exit_value) +{ + if (exit_value && !validate_write_typed(exit_value)) + return -EFAULT; + + Thread* thread = nullptr; + for_each_thread([&](auto& child_thread) { + if (child_thread.tid() == tid) { + thread = &child_thread; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + + if (!thread) + return -ESRCH; + + if (thread == current) + return -EDEADLK; + + if (thread->m_joinee == current) + return -EDEADLK; + + ASSERT(thread->m_joiner != current); + if (thread->m_joiner) + return -EINVAL; + + // FIXME: EINVAL: 'thread' is not a joinable thread + + // FIXME: pthread_join() should not be interruptable. Enforce this somehow? + auto result = current->block(*thread); + (void)result; + + // NOTE: 'thread' is very possibly deleted at this point. Clear it just to be safe. + thread = nullptr; + + if (exit_value) + *exit_value = current->m_joinee_exit_value; + return 0; +} + int Process::sys$gettid() { return current->tid(); diff --git a/Kernel/Process.h b/Kernel/Process.h index 36da7e7f08..1e912aaa61 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -206,6 +206,7 @@ public: int sys$restore_signal_mask(u32 mask); int sys$create_thread(void* (*)(void*), void*); void sys$exit_thread(void*); + int sys$join_thread(int tid, void** exit_value); int sys$rename(const char* oldpath, const char* newpath); int sys$systrace(pid_t); int sys$mknod(const char* pathname, mode_t, dev_t); diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 812ae437e1..b072fdfcde 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -68,6 +68,19 @@ void Scheduler::beep() s_beep_timeout = g_uptime + 100; } +Thread::JoinBlocker::JoinBlocker(Thread& joinee) + : m_joinee(joinee) +{ + ASSERT(m_joinee.m_joiner == nullptr); + m_joinee.m_joiner = current; + current->m_joinee = &joinee; +} + +bool Thread::JoinBlocker::should_unblock(Thread& joiner, time_t, long) +{ + return !joiner.m_joinee; +} + Thread::FileDescriptionBlocker::FileDescriptionBlocker(const FileDescription& description) : m_blocked_description(description) {} diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index c6d231ddf3..3b90b2276b 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -137,7 +137,8 @@ typedef u32 socklen_t; __ENUMERATE_SYSCALL(getrandom) \ __ENUMERATE_SYSCALL(clock_gettime) \ __ENUMERATE_SYSCALL(clock_nanosleep) \ - __ENUMERATE_SYSCALL(openat) + __ENUMERATE_SYSCALL(openat) \ + __ENUMERATE_SYSCALL(join_thread) namespace Syscall { diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 5dd341d2a6..208b14cec6 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -238,6 +238,14 @@ void Thread::finalize() dbgprintf("Finalizing Thread %u in %s(%u)\n", tid(), m_process.name().characters(), pid()); set_state(Thread::State::Dead); + if (m_joiner) { + ASSERT(m_joiner->m_joinee == this); + m_joiner->m_joinee_exit_value = m_exit_value; + m_joiner->m_joinee = nullptr; + // NOTE: We clear the joiner pointer here as well, to be tidy. + m_joiner = nullptr; + } + if (m_dump_backtrace_on_finalization) dbg() << backtrace_impl(); diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 3e2b55ed80..0d43a197d5 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -95,6 +95,16 @@ public: friend class Thread; }; + class JoinBlocker final : public Blocker { + public: + explicit JoinBlocker(Thread& joinee); + virtual bool should_unblock(Thread&, time_t now_s, long us) override; + virtual const char* state_string() const override { return "Joining"; } + + private: + Thread& m_joinee; + }; + class FileDescriptionBlocker : public Blocker { public: const FileDescription& blocked_description() const; @@ -356,6 +366,13 @@ private: VirtualAddress m_thread_specific_data; SignalActionData m_signal_action_data[32]; Blocker* m_blocker { nullptr }; + + // FIXME: Some of these could probably live in the JoinBlocker object instead. + Thread* m_joiner { nullptr }; + Thread* m_joinee { nullptr }; + void* m_joinee_exit_value { nullptr }; + void* m_exit_value { nullptr }; + FPUState* m_fpu_state { nullptr }; State m_state { Invalid }; ThreadPriority m_priority { ThreadPriority::Normal }; diff --git a/Libraries/LibPthread/pthread.cpp b/Libraries/LibPthread/pthread.cpp index 26761b8a02..b659d376d1 100644 --- a/Libraries/LibPthread/pthread.cpp +++ b/Libraries/LibPthread/pthread.cpp @@ -1,10 +1,11 @@ #include +#include #include #include extern "C" { -int pthread_create(pthread_t* thread, pthread_attr_t* attributes, void *(*start_routine)(void*), void* argument_to_start_routine) +int pthread_create(pthread_t* thread, pthread_attr_t* attributes, void* (*start_routine)(void*), void* argument_to_start_routine) { if (!thread) return -EINVAL; @@ -21,4 +22,9 @@ void pthread_exit(void* value_ptr) exit_thread(value_ptr); } +int pthread_join(pthread_t thread, void** exit_value_ptr) +{ + int rc = syscall(SC_join_thread, thread, exit_value_ptr); + __RETURN_WITH_ERRNO(rc, rc, -1); +} } diff --git a/Userland/Makefile b/Userland/Makefile index 3129a738e0..2522e2e23a 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -19,7 +19,7 @@ clean: $(APPS) : % : %.o $(OBJS) @echo "LD $@" - @$(LD) -o $@ $(LDFLAGS) $< -lc -lhtml -lgui -ldraw -laudio -lipc -lthread -lcore -lpcidb -lmarkdown + @$(LD) -o $@ $(LDFLAGS) $< -lc -lhtml -lgui -ldraw -laudio -lipc -lthread -lcore -lpcidb -lmarkdown -lpthread %.o: %.cpp @echo "CXX $<" diff --git a/Userland/tt.cpp b/Userland/tt.cpp new file mode 100644 index 0000000000..e8e56f8c8d --- /dev/null +++ b/Userland/tt.cpp @@ -0,0 +1,25 @@ +#include +#include + +int main(int, char**) +{ + printf("Hello from the first thread!\n"); + pthread_t thread_id; + int rc = pthread_create(&thread_id, nullptr, [](void*) -> void* { + printf("Hi there, from the second thread!\n"); + pthread_exit((void*)0xDEADBEEF); + return nullptr; + }, nullptr); + if (rc < 0) { + perror("pthread_create"); + return 1; + } + void* retval; + rc = pthread_join(thread_id, &retval); + if (rc < 0) { + perror("pthread_join"); + return 1; + } + printf("Okay, joined and got retval=%p\n", retval); + return 0; +}