Explorar o código

LibPthread: Implement condition variables

This feels like a pretty naive implementation, but I think it can work.
Basically each waiter creates an object on its stack that is then
added to a linked list inside by the pthread_cond_t.

Signalling is then done by walking the list and unsetting the "waiting"
flag on as many of the waiters as you like.
Andreas Kling %!s(int64=5) %!d(string=hai) anos
pai
achega
cc1ef6dadb

+ 8 - 2
Libraries/LibC/sys/types.h

@@ -66,10 +66,16 @@ typedef void* pthread_once_t;
 typedef uint32_t pthread_mutex_t;
 typedef uint32_t pthread_mutex_t;
 typedef void* pthread_attr_t;
 typedef void* pthread_attr_t;
 typedef void* pthread_mutexattr_t;
 typedef void* pthread_mutexattr_t;
-typedef void* pthread_cond_t;
+
+typedef struct __pthread_cond_t {
+    void* storage;
+} pthread_cond_t;
+
 typedef void* pthread_rwlock_t;
 typedef void* pthread_rwlock_t;
 typedef void* pthread_rwlockatrr_t;
 typedef void* pthread_rwlockatrr_t;
 typedef void* pthread_spinlock_t;
 typedef void* pthread_spinlock_t;
-typedef void* pthread_condattr_t;
+typedef struct __pthread_condattr_t {
+    int clockid; // clockid_t
+} pthread_condattr_t;
 
 
 __END_DECLS
 __END_DECLS

+ 98 - 0
Libraries/LibPthread/pthread.cpp

@@ -1,11 +1,13 @@
 #include <AK/Assertions.h>
 #include <AK/Assertions.h>
 #include <AK/Atomic.h>
 #include <AK/Atomic.h>
+#include <AK/InlineLinkedList.h>
 #include <AK/StdLibExtras.h>
 #include <AK/StdLibExtras.h>
 #include <Kernel/Syscall.h>
 #include <Kernel/Syscall.h>
 #include <limits.h>
 #include <limits.h>
 #include <pthread.h>
 #include <pthread.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <sys/mman.h>
 #include <sys/mman.h>
+#include <time.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
 #define PTHREAD_DEBUG
 #define PTHREAD_DEBUG
@@ -327,4 +329,100 @@ int pthread_attr_setstacksize(pthread_attr_t* attributes, size_t stack_size)
 
 
     return 0;
     return 0;
 }
 }
+
+struct WaitNode : public InlineLinkedListNode<WaitNode> {
+    bool waiting { true };
+    WaitNode* m_next { nullptr };
+    WaitNode* m_prev { nullptr };
+};
+
+struct ConditionVariable {
+    InlineLinkedList<WaitNode> waiters;
+    clockid_t clock;
+};
+
+int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr)
+{
+    auto& condvar = *new ConditionVariable;
+    cond->storage = &condvar;
+    if (attr)
+        condvar.clock = attr->clockid;
+    return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t* cond)
+{
+    delete static_cast<ConditionVariable*>(cond->storage);
+    return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex)
+{
+    WaitNode node;
+    auto& condvar = *(ConditionVariable*)cond->storage;
+    condvar.waiters.append(&node);
+    while (node.waiting) {
+        pthread_mutex_unlock(mutex);
+        sched_yield();
+        pthread_mutex_lock(mutex);
+    }
+    return 0;
+}
+
+int pthread_condattr_init(pthread_condattr_t* attr)
+{
+    attr->clockid = CLOCK_MONOTONIC;
+    return 0;
+}
+
+int pthread_condattr_destroy(pthread_condattr_t*)
+{
+    return 0;
+}
+
+int pthread_condattr_setclock(pthread_condattr_t* attr, clockid_t clock)
+{
+    attr->clockid = clock;
+    return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime)
+{
+    WaitNode node;
+    auto& condvar = *(ConditionVariable*)cond->storage;
+    condvar.waiters.append(&node);
+    while (node.waiting) {
+        struct timespec now;
+        if (clock_gettime(condvar.clock, &now) < 0)
+            return -1;
+        if ((abstime->tv_sec < now.tv_sec) || (abstime->tv_sec == now.tv_sec && abstime->tv_nsec <= now.tv_nsec)) {
+            errno = ETIMEDOUT;
+            return -1;
+        }
+        pthread_mutex_unlock(mutex);
+        sched_yield();
+        pthread_mutex_lock(mutex);
+    }
+    return 0;
+}
+
+int pthread_cond_signal(pthread_cond_t* cond)
+{
+    auto& condvar = *(ConditionVariable*)cond->storage;
+    if (condvar.waiters.is_empty())
+        return 0;
+    auto* node = condvar.waiters.remove_head();
+    node->waiting = false;
+    return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t* cond)
+{
+    auto& condvar = *(ConditionVariable*)cond->storage;
+    while (!condvar.waiters.is_empty()) {
+        auto* node = condvar.waiters.remove_head();
+        node->waiting = false;
+    }
+    return 0;
+}
 }
 }

+ 3 - 0
Libraries/LibPthread/pthread.h

@@ -4,6 +4,7 @@
 #include <stdint.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <sys/types.h>
+#include <time.h>
 
 
 __BEGIN_DECLS
 __BEGIN_DECLS
 
 
@@ -56,6 +57,8 @@ int pthread_cond_broadcast(pthread_cond_t*);
 int pthread_cond_init(pthread_cond_t*, const pthread_condattr_t*);
 int pthread_cond_init(pthread_cond_t*, const pthread_condattr_t*);
 int pthread_cond_signal(pthread_cond_t*);
 int pthread_cond_signal(pthread_cond_t*);
 int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
 int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
+int pthread_condattr_init(pthread_condattr_t*);
+int pthread_condattr_setclock(pthread_condattr_t*, clockid_t);
 int pthread_condattr_destroy(pthread_condattr_t*);
 int pthread_condattr_destroy(pthread_condattr_t*);
 int pthread_cancel(pthread_t);
 int pthread_cancel(pthread_t);
 int pthread_cond_destroy(pthread_cond_t*);
 int pthread_cond_destroy(pthread_cond_t*);