Bladeren bron

Kernel+LibPthread: Implement pthread_join()

It's now possible to block until another thread in the same process has
exited. We can also retrieve its exit value, which is whatever value it
passed to pthread_exit(). :^)
Andreas Kling 5 jaren geleden
bovenliggende
commit
69efa3f630
9 gewijzigde bestanden met toevoegingen van 117 en 5 verwijderingen
  1. 43 2
      Kernel/Process.cpp
  2. 1 0
      Kernel/Process.h
  3. 13 0
      Kernel/Scheduler.cpp
  4. 2 1
      Kernel/Syscall.h
  5. 8 0
      Kernel/Thread.cpp
  6. 17 0
      Kernel/Thread.h
  7. 7 1
      Libraries/LibPthread/pthread.cpp
  8. 1 1
      Userland/Makefile
  9. 25 0
      Userland/tt.cpp

+ 43 - 2
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 (&current->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::JoinBlocker>(*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();

+ 1 - 0
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);

+ 13 - 0
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)
 {}

+ 2 - 1
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 {
 

+ 8 - 0
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();
 

+ 17 - 0
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 };

+ 7 - 1
Libraries/LibPthread/pthread.cpp

@@ -1,10 +1,11 @@
 #include <AK/StdLibExtras.h>
+#include <Kernel/Syscall.h>
 #include <pthread.h>
 #include <unistd.h>
 
 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);
+}
 }

+ 1 - 1
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 $<"

+ 25 - 0
Userland/tt.cpp

@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <stdio.h>
+
+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;
+}