summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorkleines Filmröllchen <malu.bertsch@gmail.com>2022-01-02 14:49:53 +0100
committerAndreas Kling <kling@serenityos.org>2022-01-23 15:21:10 +0100
commit3d6e08156d9ceff0f76d878f0f900a78c36685b6 (patch)
treeff6df52b4a223410974f8f80dce33ccd5c1aea2f /Userland
parenta7ce0c2297e316963100babeb462bcc41c550de9 (diff)
downloadserenity-3d6e08156d9ceff0f76d878f0f900a78c36685b6.zip
LibThreading: Introduce MutexProtected generic synchronization primitive
MutexProtected mirrors the identically-named Kernel primitive and can be used to synchronize access to any object that might not be thread safe on its own. Synchronization is done with a simple mutex, so access to a MutexProtected object is potentially blocking. Mutex now has an internal nesting variable which is there to harden it against lock-unlock ordering issues (e.g. double unlocking).
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibThreading/Mutex.h21
-rw-r--r--Userland/Libraries/LibThreading/MutexProtected.h56
2 files changed, 75 insertions, 2 deletions
diff --git a/Userland/Libraries/LibThreading/Mutex.h b/Userland/Libraries/LibThreading/Mutex.h
index 44921eab61..9ffb061306 100644
--- a/Userland/Libraries/LibThreading/Mutex.h
+++ b/Userland/Libraries/LibThreading/Mutex.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -7,6 +8,7 @@
#pragma once
#include <AK/Assertions.h>
+#include <AK/Format.h>
#include <AK/Noncopyable.h>
#include <AK/Types.h>
#include <pthread.h>
@@ -20,6 +22,7 @@ class Mutex {
public:
Mutex()
+ : m_lock_count(0)
{
#ifndef __serenity__
pthread_mutexattr_t attr;
@@ -28,7 +31,11 @@ public:
pthread_mutex_init(&m_mutex, &attr);
#endif
}
- ~Mutex() = default;
+ ~Mutex()
+ {
+ VERIFY(m_lock_count == 0);
+ // FIXME: pthread_mutex_destroy() is not implemented.
+ }
void lock();
void unlock();
@@ -39,6 +46,7 @@ private:
#else
pthread_mutex_t m_mutex;
#endif
+ unsigned m_lock_count { 0 };
};
class MutexLocker {
@@ -51,7 +59,10 @@ public:
{
lock();
}
- ALWAYS_INLINE ~MutexLocker() { unlock(); }
+ ALWAYS_INLINE ~MutexLocker()
+ {
+ unlock();
+ }
ALWAYS_INLINE void unlock() { m_mutex.unlock(); }
ALWAYS_INLINE void lock() { m_mutex.lock(); }
@@ -62,10 +73,16 @@ private:
ALWAYS_INLINE void Mutex::lock()
{
pthread_mutex_lock(&m_mutex);
+ m_lock_count++;
}
ALWAYS_INLINE void Mutex::unlock()
{
+ VERIFY(m_lock_count > 0);
+ // FIXME: We need to protect the lock count with the mutex itself.
+ // This may be bad because we're not *technically* unlocked yet,
+ // but we're not handling any errors from pthread_mutex_unlock anyways.
+ m_lock_count--;
pthread_mutex_unlock(&m_mutex);
}
diff --git a/Userland/Libraries/LibThreading/MutexProtected.h b/Userland/Libraries/LibThreading/MutexProtected.h
new file mode 100644
index 0000000000..f12d2fea41
--- /dev/null
+++ b/Userland/Libraries/LibThreading/MutexProtected.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022, kleines Filmröllchen <malu.bertsch@gmail.com>.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Concepts.h>
+#include <AK/Noncopyable.h>
+#include <LibThreading/Mutex.h>
+
+namespace Threading {
+
+template<typename T>
+class MutexProtected {
+ AK_MAKE_NONCOPYABLE(MutexProtected);
+ AK_MAKE_NONMOVABLE(MutexProtected);
+ using ProtectedType = T;
+
+public:
+ ALWAYS_INLINE MutexProtected() = default;
+ ALWAYS_INLINE MutexProtected(T&& value)
+ : m_value(move(value))
+ {
+ }
+ ALWAYS_INLINE explicit MutexProtected(T& value)
+ : m_value(value)
+ {
+ }
+
+ template<typename Callback>
+ decltype(auto) with_locked(Callback callback)
+ {
+ auto lock = this->lock();
+ // This allows users to get a copy, but if we don't allow references through &m_value, it's even more complex.
+ return callback(m_value);
+ }
+
+ template<VoidFunction<T> Callback>
+ void for_each_locked(Callback callback)
+ {
+ with_locked([&](auto& value) {
+ for (auto& item : value)
+ callback(item);
+ });
+ }
+
+private:
+ [[nodiscard]] ALWAYS_INLINE MutexLocker lock() { return MutexLocker(m_lock); }
+
+ T m_value;
+ Mutex m_lock {};
+};
+
+}