Parcourir la source

Simplify thread and mutex wrapper, add spinlock

Kajetan Johannes Hammerle il y a 4 semaines
Parent
commit
dc2b3b3c64

+ 3 - 1
CMakeLists.txt

@@ -21,6 +21,7 @@ set(SRC
     "src/View.cpp"
     "src/Thread.cpp"
     "src/Mutex.cpp"
+    "src/SpinLock.cpp"
     "src/FileReader.cpp"
     "src/ErrorSimulator.cpp"
     "src/ArrayString.cpp"
@@ -129,7 +130,8 @@ target_compile_options(core PUBLIC
     -fno-exceptions
     -fno-rtti
     -fno-threadsafe-statics
-    -nostdinc++
+    # deactivated due to the need for <atomic>
+    #-nostdinc++
     -pedantic
     -pedantic-errors
     -Wall

+ 6 - 7
include/core/thread/Mutex.hpp

@@ -1,13 +1,14 @@
 #ifndef CORE_MUTEX_HPP
 #define CORE_MUTEX_HPP
 
-#include "core/utils/AlignedData.hpp"
+#include <threads.h>
+
 #include "core/utils/Check.hpp"
 #include "core/utils/Error.hpp"
 
 namespace Core {
     class Mutex final {
-        AlignedData<40, 8> mutex;
+        mtx_t mutex;
 
     public:
         Mutex();
@@ -16,11 +17,9 @@ namespace Core {
         ~Mutex();
         Mutex& operator=(const Mutex& other) = delete;
         Mutex& operator=(Mutex&& other) = delete;
-
-        check_return Error init();
-
-        check_return Error lock();
-        check_return Error unlock();
+        CError init();
+        CError lock();
+        CError unlock();
     };
 }
 

+ 24 - 0
include/core/thread/SpinLock.hpp

@@ -0,0 +1,24 @@
+#ifndef CORE_SPIN_LOCK_HPP
+#define CORE_SPIN_LOCK_HPP
+
+// stdatomic.h is not compatible with C++
+// all calls are noexcept
+#include <atomic>
+using atomic_bool = std::atomic_bool;
+
+namespace Core {
+    class SpinLock final {
+        atomic_bool locked;
+
+    public:
+        SpinLock();
+        SpinLock(const SpinLock& other) = delete;
+        SpinLock(SpinLock&& other) = delete;
+        SpinLock& operator=(const SpinLock& other) = delete;
+        SpinLock& operator=(SpinLock&& other) = delete;
+        void lock();
+        void unlock();
+    };
+}
+
+#endif

+ 11 - 32
src/Mutex.cpp

@@ -1,47 +1,26 @@
 #include "core/thread/Mutex.hpp"
 
 #include <string.h>
-#include <threads.h>
-
-#include "core/utils/Meta.hpp"
-#include "core/utils/Utility.hpp"
-
-static void reset(mtx_t* m) {
-    memset(m, 0, sizeof(mtx_t));
-}
 
 Core::Mutex::Mutex() : mutex() {
-    CORE_ASSERT_ALIGNED_DATA(mutex, mtx_t);
-    reset(mutex.as<mtx_t>());
-}
-
-static bool doesExist(mtx_t* t) {
-    mtx_t zero{};
-    return memcmp(&zero, t, sizeof(mtx_t)) != 0;
+    memset(&mutex, 0, sizeof(mutex));
 }
 
 Core::Mutex::~Mutex() {
-    if(doesExist(mutex.as<mtx_t>())) {
-        mtx_destroy(mutex.as<mtx_t>());
-    }
+    mtx_destroy(&mutex);
 }
 
-check_return Core::Error Core::Mutex::init() {
-    if(doesExist(mutex.as<mtx_t>())) {
-        return ErrorCode::INVALID_STATE;
-    }
-    return mtx_init(mutex.as<mtx_t>(), mtx_plain) != thrd_success
-               ? ErrorCode::MUTEX_ERROR
-               : ErrorCode::NONE;
+CError Core::Mutex::init() {
+    return mtx_init(&mutex, mtx_plain) != thrd_success ? ErrorCode::MUTEX_ERROR
+                                                       : ErrorCode::NONE;
 }
 
-check_return Core::Error Core::Mutex::lock() {
-    return mtx_lock(mutex.as<mtx_t>()) != thrd_success ? ErrorCode::MUTEX_ERROR
-                                                       : ErrorCode::NONE;
+CError Core::Mutex::lock() {
+    return mtx_lock(&mutex) != thrd_success ? ErrorCode::MUTEX_ERROR
+                                            : ErrorCode::NONE;
 }
 
-check_return Core::Error Core::Mutex::unlock() {
-    return mtx_unlock(mutex.as<mtx_t>()) != thrd_success
-               ? ErrorCode::MUTEX_ERROR
-               : ErrorCode::NONE;
+CError Core::Mutex::unlock() {
+    return mtx_unlock(&mutex) != thrd_success ? ErrorCode::MUTEX_ERROR
+                                              : ErrorCode::NONE;
 }

+ 22 - 0
src/SpinLock.cpp

@@ -0,0 +1,22 @@
+#include "core/thread/SpinLock.hpp"
+
+#include <threads.h>
+
+Core::SpinLock::SpinLock() : locked() {
+    atomic_init(&locked, false);
+}
+
+void Core::SpinLock::lock() {
+    while(true) {
+        bool expected = false;
+        if(atomic_compare_exchange_weak(&locked, &expected, true)) {
+            break;
+        }
+        timespec s{0, 0};
+        thrd_sleep(&s, nullptr);
+    }
+}
+
+void Core::SpinLock::unlock() {
+    atomic_store(&locked, false);
+}

+ 1 - 0
test/modules/ArrayStringTests.cpp

@@ -428,6 +428,7 @@ static void testPrint() {
     s.append(L'\u8000');
     s.append(U'\U00100000');
     CORE_TEST_EQUAL(build("\u0040\u0400\u8000\U00100000"), s);
+    s.append('\n');
     s.print();
 }
 

+ 44 - 1
test/modules/ThreadTests.cpp

@@ -2,7 +2,9 @@
 
 #include "../Tests.hpp"
 #include "core/thread/Mutex.hpp"
+#include "core/thread/SpinLock.hpp"
 #include "core/thread/Thread.hpp"
+#include "core/utils/Clock.hpp"
 
 static int runDone = 0;
 
@@ -94,15 +96,55 @@ static int incrementMutexCounter(void* p) {
 }
 
 static void testMutex() {
+    Core::Clock::Nanos n;
+    CORE_TEST_ERROR(Core::Clock::getNanos(n));
+
     MutexCounter mc;
     CORE_TEST_ERROR(mc.m.init());
-    CORE_TEST_EQUAL(Core::ErrorCode::INVALID_STATE, mc.m.init());
     Core::Thread t[2];
     CORE_TEST_ERROR(t[0].start(incrementMutexCounter, &mc));
     CORE_TEST_ERROR(t[1].start(incrementMutexCounter, &mc));
     CORE_TEST_ERROR(t[0].join(nullptr));
     CORE_TEST_ERROR(t[1].join(nullptr));
     CORE_TEST_EQUAL(20000, mc.counter);
+
+    Core::Clock::Nanos n2;
+    CORE_TEST_ERROR(Core::Clock::getNanos(n2));
+    Core::ArrayString<64> s;
+    s.append(n2 - n).append("ns Mutex").printLine();
+}
+
+struct SpinLockCounter {
+    Core::SpinLock s{};
+    int counter = 0;
+};
+
+static int incrementSpinLockCounter(void* p) {
+    SpinLockCounter* mcp = static_cast<SpinLockCounter*>(p);
+    for(int i = 0; i < 10000; i++) {
+        mcp->s.lock();
+        mcp->counter++;
+        mcp->s.unlock();
+    }
+    return 0;
+}
+
+static void testSpinLock() {
+    Core::Clock::Nanos n;
+    CORE_TEST_ERROR(Core::Clock::getNanos(n));
+
+    SpinLockCounter sc;
+    Core::Thread t[2];
+    CORE_TEST_ERROR(t[0].start(incrementSpinLockCounter, &sc));
+    CORE_TEST_ERROR(t[1].start(incrementSpinLockCounter, &sc));
+    CORE_TEST_ERROR(t[0].join(nullptr));
+    CORE_TEST_ERROR(t[1].join(nullptr));
+    CORE_TEST_EQUAL(20000, sc.counter);
+
+    Core::Clock::Nanos n2;
+    CORE_TEST_ERROR(Core::Clock::getNanos(n2));
+    Core::ArrayString<64> s;
+    s.append(n2 - n).append("ns SpinLock").printLine();
 }
 
 void Core::testThread() {
@@ -115,4 +157,5 @@ void Core::testThread() {
     testMoveIntoActive();
     testDoubleJoin();
     testMutex();
+    testSpinLock();
 }