Browse Source

Basic thread wrapper

Kajetan Johannes Hammerle 1 year ago
parent
commit
a965804531
8 changed files with 144 additions and 3 deletions
  1. 4 1
      meson.build
  2. 4 1
      test/Main.cpp
  3. 44 0
      tests/ThreadTests.cpp
  4. 8 0
      tests/ThreadTests.h
  5. 32 0
      thread/Thread.cpp
  6. 16 0
      thread/Thread.h
  7. 19 0
      utils/Utility.cpp
  8. 17 1
      utils/Utility.h

+ 4 - 1
meson.build

@@ -16,6 +16,7 @@ src = [
     'math/Plane.cpp',
     'math/Frustum.cpp',
     'math/View.cpp',
+    'thread/Thread.cpp',
 ]
 
 src_tests = [
@@ -47,6 +48,7 @@ src_tests = [
     'tests/BufferTests.cpp',
     'tests/ClockTests.cpp',
     'tests/RandomTests.cpp',
+    'tests/ThreadTests.cpp',
 ]
 
 compiler = meson.get_compiler('cpp')
@@ -70,9 +72,10 @@ compile_args = compiler.get_supported_arguments([
     '-nostdinc++', '-fno-exceptions', '-fno-rtti', '-fno-threadsafe-statics'
 ])
 link_args = compiler.get_supported_arguments([
-    '-nodefaultlibs', '-lc', '-lm'
+    '-nodefaultlibs', '-lc', '-lm', '-lpthread'
 ])
 
+
 core_include = [include_directories('.')]
 core = static_library('core', 
     sources: src,

+ 4 - 1
test/Main.cpp

@@ -21,6 +21,7 @@
 #include "tests/RandomTests.h"
 #include "tests/RingBufferTests.h"
 #include "tests/StackTests.h"
+#include "tests/ThreadTests.h"
 #include "tests/UniquePointerTests.h"
 #include "tests/UtilityTests.h"
 #include "tests/VectorTests.h"
@@ -42,7 +43,8 @@ static void onError(const char* file, int line, Core::Error e, void*) {
        e == Core::Error::INVALID_REMOVE_INDEX) {
         return;
     }
-    CORE_LOG_ERROR("Error in #:# - #", file, line, static_cast<int>(e));
+    CORE_LOG_ERROR("Error in #:# - #:#", file, line, static_cast<int>(e),
+                   Core::getErrorName(e));
 }
 
 int main() {
@@ -70,6 +72,7 @@ int main() {
     Core::RandomTests::test();
     Core::RingBufferTests::test();
     Core::StackTests::test();
+    Core::ThreadTests::test();
     Core::UniquePointerTests::test();
     Core::UtilityTests::test();
     Core::VectorTests::test();

+ 44 - 0
tests/ThreadTests.cpp

@@ -0,0 +1,44 @@
+#include "tests/ThreadTests.h"
+
+#include "test/Test.h"
+#include "thread/Thread.h"
+
+static int runDone = 0;
+
+struct IntHolder {
+    int value;
+};
+
+static int run(void*) {
+    runDone = 1;
+    return 7;
+}
+
+static void testStart() {
+    runDone = 0;
+    Core::Thread::Id id = Core::Thread::start(run, nullptr);
+    CORE_TEST_FALSE(id == Core::Thread::ERROR);
+    int returnValue = 0;
+    CORE_TEST_FALSE(Core::Thread::join(id, &returnValue));
+    CORE_TEST_EQUAL(1, runDone);
+    CORE_TEST_EQUAL(7, returnValue);
+}
+
+static void testLambda() {
+    IntHolder i(0);
+    Core::Thread::Id id = Core::Thread::start(
+        [](void* p) {
+            IntHolder* ip = static_cast<IntHolder*>(p);
+            ip->value = 2;
+            return 0;
+        },
+        &i);
+    CORE_TEST_FALSE(id == Core::Thread::ERROR);
+    CORE_TEST_FALSE(Core::Thread::join(id, nullptr));
+    CORE_TEST_EQUAL(2, i.value);
+}
+
+void Core::ThreadTests::test() {
+    testStart();
+    testLambda();
+}

+ 8 - 0
tests/ThreadTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_THREAD_TESTS_H
+#define CORE_THREAD_TESTS_H
+
+namespace Core::ThreadTests {
+    void test();
+}
+
+#endif

+ 32 - 0
thread/Thread.cpp

@@ -0,0 +1,32 @@
+#include "thread/Thread.h"
+
+#include <threads.h>
+
+#include "data/HashMap.h"
+
+static Core::HashMap<Core::Thread::Id, thrd_t> threads;
+static Core::Thread::Id idCounter = 0;
+
+Core::Thread::Id Core::Thread::start(Function f, void* p) {
+    Id id = idCounter++;
+    thrd_t* t = threads.tryEmplace(id);
+    if(t == nullptr) {
+        return ERROR;
+    }
+    if(thrd_create(t, f, p) != thrd_success) {
+        (void)threads.remove(id);
+        return CORE_ERROR(Core::Error::THREAD_ERROR);
+    }
+    return id;
+}
+
+bool Core::Thread::join(Id id, int* returnValue) {
+    thrd_t* t = threads.search(id);
+    if(t == nullptr) {
+        return CORE_ERROR(Core::Error::INVALID_ID);
+    }
+    if(thrd_join(*t, returnValue) != thrd_success) {
+        return CORE_ERROR(Core::Error::THREAD_ERROR);
+    }
+    return threads.remove(id);
+}

+ 16 - 0
thread/Thread.h

@@ -0,0 +1,16 @@
+#ifndef CORE_THREAD_H
+#define CORE_THREAD_H
+
+#include "utils/Check.h"
+
+namespace Core::Thread {
+    using Id = int;
+    constexpr Id ERROR = -1;
+    using Function = int (*)(void*);
+    // returns Core::Thread::ERROR on error and calls the error callback
+    check_return Id start(Function f, void* p);
+    // returns true on error and calls the error callback
+    check_return bool join(Id id, int* returnValue = nullptr);
+}
+
+#endif

+ 19 - 0
utils/Utility.cpp

@@ -9,6 +9,25 @@ static void* errorData = nullptr;
 static Core::ExitHandler exitHandler = nullptr;
 static void* exitData = nullptr;
 
+const char* Core::getErrorName(Error e) {
+    switch(e) {
+        case Error::NONE: return "NONE";
+        case Error::NEGATIVE_ARGUMENT: return "NEGATIVE_ARGUMENT";
+        case Error::CAPACITY_REACHED: return "CAPACITY_REACHED";
+        case Error::BLOCKED_STDOUT: return "BLOCKED_STDOUT";
+        case Error::OUT_OF_MEMORY: return "OUT_OF_MEMORY";
+        case Error::INVALID_CHAR: return "INVALID_CHAR";
+        case Error::NOT_FOUND: return "NOT_FOUND";
+        case Error::INVALID_INDEX: return "INVALID_INDEX";
+        case Error::INVALID_REMOVE_INDEX: return "INVALID_REMOVE_INDEX";
+        case Error::TIME_NOT_AVAILABLE: return "TIME_NOT_AVAILABLE";
+        case Error::SLEEP_INTERRUPTED: return "SLEEP_INTERRUPTED";
+        case Error::THREAD_ERROR: return "THREAD_ERROR";
+        case Error::INVALID_ID: return "INVALID_ID";
+    }
+    return "?";
+}
+
 bool Core::handleError(const char* file, int line, Error e, bool check) {
     if(check && errorHandler != nullptr) {
         errorHandler(file, line, e, errorData);

+ 17 - 1
utils/Utility.h

@@ -17,8 +17,11 @@ namespace Core {
         INVALID_INDEX,
         INVALID_REMOVE_INDEX,
         TIME_NOT_AVAILABLE,
-        SLEEP_INTERRUPTED
+        SLEEP_INTERRUPTED,
+        THREAD_ERROR,
+        INVALID_ID
     };
+    const char* getErrorName(Error e);
 
     using ErrorHandler = void (*)(const char*, int, Error, void*);
     bool handleError(const char* file, int line, Error e, bool check = true);
@@ -27,6 +30,16 @@ namespace Core {
     Core::handleError(__FILE__, __LINE__, error __VA_OPT__(, ) __VA_ARGS__)
 
     namespace Internal {
+        template<typename T>
+        struct BaseRemovePointer final {
+            using Type = T;
+        };
+
+        template<typename T>
+        struct BaseRemovePointer<T*> final {
+            using Type = T;
+        };
+
         template<typename T>
         struct BaseRemoveReference final {
             using Type = T;
@@ -63,6 +76,9 @@ namespace Core {
         };
     }
 
+    template<typename T>
+    using RemovePointer = Internal::BaseRemovePointer<T>::Type;
+
     template<typename T>
     using RemoveReference = Internal::BaseRemoveReference<T>::Type;