[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/3] pthread: Thread-Keys



Ein pthread-Key ist ein globaler Key für den gesamten Prozess, wobei
aber jeder Thread unterschiedliche Daten sieht.

Für einen Key kann eine Destruktorfunktion registriert werden, die beim
Beenden eines Threads aufgerufen wird, um die jeweils gespeicherten
Daten freizugeben.  Dafür brauchen wir einen Notifier, der von
exit_thread() aufgerufen wird.

Signed-off-by: Hanna Reitz <hanna.reitz@xxxxxxxxxxx>
---
 src/modules/include/pthread.h            |   9 +
 src/modules/lib/posix/pthread/keys.c     | 258 +++++++++++++++++++++++
 src/modules/lib/syscalls/create_thread.c |   5 +
 3 files changed, 272 insertions(+)
 create mode 100644 src/modules/lib/posix/pthread/keys.c

diff --git a/src/modules/include/pthread.h b/src/modules/include/pthread.h
index 517e01c0..1516e39f 100644
--- a/src/modules/include/pthread.h
+++ b/src/modules/include/pthread.h
@@ -51,6 +51,8 @@ typedef struct {
 // pthread_rwlock_init()
 typedef unsigned int pthread_rwlockattr_t;
 
+typedef unsigned int pthread_key_t;
+
 
 int pthread_create(pthread_t *thread,
     __attribute__((unused)) const pthread_attr_t *attr,
@@ -69,4 +71,11 @@ int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
 int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
 
+
+int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
+int pthread_key_delete(pthread_key_t key);
+
+int pthread_setspecific(pthread_key_t key, const void* value);
+void* pthread_getspecific(pthread_key_t key);
+
 #endif
diff --git a/src/modules/lib/posix/pthread/keys.c b/src/modules/lib/posix/pthread/keys.c
new file mode 100644
index 00000000..6933434f
--- /dev/null
+++ b/src/modules/lib/posix/pthread/keys.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2021 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Hanna Reitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct thread_key {
+    void (*destructor)(void*);
+    // Index: pthread_self()
+    const void** values;
+    size_t value_count;
+
+    // Bezieht sich nur auf das thread_key-Objekt, nicht auf values[].
+    // Wenn etwas innerhalb von values[] veraendert wird, braucht man
+    // also nur ein rdlock.
+    pthread_rwlock_t rwl;
+};
+
+// Index: pthread_key_t - 1
+// (Ein Key darf wohl nicht 0 sein)
+static struct thread_key** thread_keys;
+static uint32_t thread_key_count;
+
+// Bezieht sich nur auf thread_keys und thread_key_count, nicht auf
+// thread_keys[].  Wenn etwas innerhalb von thread_keys[] veraendert
+// wird, braucht man also nur ein rdlock.  (Fuer *thread_keys[]
+// braucht man das Lock hier nicht mal, nur thread_key.rwl.)
+static pthread_rwlock_t thread_keys_rwl = PTHREAD_RWLOCK_INITIALIZER;
+
+int pthread_key_create(pthread_key_t* key, void (*destructor)(void*))
+{
+    uint32_t ikey;
+    int ret;
+
+    ret = pthread_rwlock_wrlock(&thread_keys_rwl);
+    if (ret) {
+        return ret;
+    }
+
+    ikey = thread_key_count++;
+    thread_keys = realloc(thread_keys,
+                          thread_key_count * sizeof(thread_keys[0]));
+
+    thread_keys[ikey] = malloc(sizeof(*thread_keys[ikey]));
+    *thread_keys[ikey] = (struct thread_key) {
+        .destructor = destructor,
+        .rwl        = PTHREAD_RWLOCK_INITIALIZER,
+    };
+
+    pthread_rwlock_unlock(&thread_keys_rwl);
+
+    *key = ikey + 1;
+    return 0;
+}
+
+int pthread_key_delete(pthread_key_t key)
+{
+    struct thread_key* key_obj;
+    int ret;
+
+    ret = pthread_rwlock_rdlock(&thread_keys_rwl);
+    if (ret) {
+        return ret;
+    }
+
+    key -= 1; // Unsigned, 0 - 1 laeuft also unter
+    if (key >= thread_key_count) {
+        pthread_rwlock_unlock(&thread_keys_rwl);
+        return EINVAL;
+    }
+
+    key_obj = thread_keys[key];
+
+    pthread_rwlock_unlock(&thread_keys_rwl);
+    ret = pthread_rwlock_wrlock(&key_obj->rwl);
+    if (ret) {
+        return ret;
+    }
+
+    free(key_obj->values);
+    key_obj->destructor = NULL;
+    key_obj->values = NULL;
+    key_obj->value_count = 0;
+
+    pthread_rwlock_unlock(&key_obj->rwl);
+
+    return 0;
+}
+
+void __pthread_notify_exit(void)
+{
+    pthread_t self = pthread_self();
+    bool destructors_called;
+    uint32_t key;
+
+    do {
+        destructors_called = false;
+
+        if (pthread_rwlock_rdlock(&thread_keys_rwl)) {
+            // Wenn das nicht klappt, koennen wir auch aufgeben
+            return;
+        }
+
+        for (key = 0; key < thread_key_count; key++) {
+            struct thread_key* key_obj = thread_keys[key];
+            const void* value = NULL;
+            void (*destructor)(void*);
+
+            pthread_rwlock_unlock(&thread_keys_rwl);
+
+            if (pthread_rwlock_rdlock(&key_obj->rwl)) {
+                if (pthread_rwlock_rdlock(&thread_keys_rwl)) {
+                    return;
+                } else {
+                    continue;
+                }
+            }
+
+            destructor = key_obj->destructor;
+
+            // Wenn kein Destruktor gesetzt wurde, vollstaendig ignorieren
+            if (destructor && self < key_obj->value_count) {
+                value = key_obj->values[self];
+                key_obj->values[self] = NULL;
+            }
+
+            pthread_rwlock_unlock(&key_obj->rwl);
+
+            if (value) {
+                destructor((void*) value);
+                destructors_called = true;
+            }
+
+            if (pthread_rwlock_rdlock(&thread_keys_rwl)) {
+                return;
+            }
+        }
+
+        pthread_rwlock_unlock(&thread_keys_rwl);
+    } while (destructors_called);
+}
+
+int pthread_setspecific(pthread_key_t key, const void* value)
+{
+    pthread_t self = pthread_self();
+    struct thread_key* key_obj;
+    int ret;
+
+    ret = pthread_rwlock_rdlock(&thread_keys_rwl);
+    if (ret) {
+        return ret;
+    }
+
+    key -= 1; // Unsigned, 0 - 1 laeuft also unter
+    if (key >= thread_key_count) {
+        pthread_rwlock_unlock(&thread_keys_rwl);
+        return EINVAL;
+    }
+
+    key_obj = thread_keys[key];
+
+    pthread_rwlock_unlock(&thread_keys_rwl);
+    ret = pthread_rwlock_rdlock(&key_obj->rwl);
+    if (ret) {
+        return ret;
+    }
+
+    if (self >= key_obj->value_count) {
+        pthread_rwlock_unlock(&key_obj->rwl);
+        ret = pthread_rwlock_wrlock(&key_obj->rwl);
+        if (ret) {
+            return ret;
+        }
+
+        // Nochmal pruefen, koennte sich ja geaendert haben
+        if (self >= key_obj->value_count) {
+            size_t nvc = self + 1;
+
+            key_obj->values = realloc(key_obj->values,
+                                      nvc * sizeof(key_obj->values[0]));
+            memset(key_obj->values + key_obj->value_count,
+                   0,
+                   (nvc - key_obj->value_count) * sizeof(key_obj->values[0]));
+
+            key_obj->value_count = nvc;
+        }
+    }
+
+    key_obj->values[self] = value;
+
+    pthread_rwlock_unlock(&key_obj->rwl);
+
+    return 0;
+}
+
+void* pthread_getspecific(pthread_key_t key)
+{
+    pthread_t self = pthread_self();
+    struct thread_key* key_obj;
+    const void* value;
+
+    if (pthread_rwlock_rdlock(&thread_keys_rwl)) {
+        return NULL;
+    }
+
+    key -= 1; // Unsigned, 0 - 1 laeuft also unter
+    if (key >= thread_key_count) {
+        pthread_rwlock_unlock(&thread_keys_rwl);
+        return NULL;
+    }
+
+    key_obj = thread_keys[key];
+
+    pthread_rwlock_unlock(&thread_keys_rwl);
+    if (pthread_rwlock_rdlock(&key_obj->rwl)) {
+        return NULL;
+    }
+
+    if (self < key_obj->value_count) {
+        value = key_obj->values[self];
+    } else {
+        value = NULL;
+    }
+
+    pthread_rwlock_unlock(&key_obj->rwl);
+
+    return (void*) value;
+}
diff --git a/src/modules/lib/syscalls/create_thread.c b/src/modules/lib/syscalls/create_thread.c
index 5789df58..111b7ea8 100644
--- a/src/modules/lib/syscalls/create_thread.c
+++ b/src/modules/lib/syscalls/create_thread.c
@@ -30,6 +30,9 @@
 #include <stdlib.h>
 #include "syscall.h"
 
+// Wird von unserer pthread-Implementierung bereitgestellt
+extern void __pthread_notify_exit(void);
+
 /**
  * ID des aktuellen Threads abfragen.
  *
@@ -103,6 +106,8 @@ tid_t create_thread(uint32_t start, void *arg)
 
 void exit_thread(void)
 {
+    __pthread_notify_exit();
+
     asm(
         "mov %0, %%eax;"
         "int $0x30;"
-- 
2.31.0