summaryrefslogtreecommitdiff
path: root/LibGUI
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-03-25 13:03:49 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-03-25 13:03:49 +0100
commit500df578fe5b24d14062d030c5628cbb4b69d3a6 (patch)
treee484af9a08e7463e95e947f8e8af3b94fb3d6645 /LibGUI
parent108b663618e893c804c150205a21f2e8d95088c2 (diff)
downloadserenity-500df578fe5b24d14062d030c5628cbb4b69d3a6.zip
LibGUI+Kernel: Add a GLock class (userspace mutex.)
It's basically a userspace port of the kernel's Lock class. Added gettid() and donate() syscalls to support the timeslice donation feature we already enjoyed in the kernel.
Diffstat (limited to 'LibGUI')
-rw-r--r--LibGUI/GLock.h106
1 files changed, 106 insertions, 0 deletions
diff --git a/LibGUI/GLock.h b/LibGUI/GLock.h
new file mode 100644
index 0000000000..7180ecca2a
--- /dev/null
+++ b/LibGUI/GLock.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <AK/Assertions.h>
+#include <AK/Types.h>
+#include <unistd.h>
+
+#define memory_barrier() asm volatile ("" ::: "memory")
+
+static inline dword CAS(volatile dword* mem, dword newval, dword oldval)
+{
+ dword ret;
+ asm volatile(
+ "cmpxchgl %2, %1"
+ :"=a"(ret), "+m"(*mem)
+ :"r"(newval), "0"(oldval)
+ :"cc", "memory");
+ return ret;
+}
+
+class GLock {
+public:
+ GLock() { }
+ ~GLock() { }
+
+ void lock();
+ void unlock();
+
+private:
+ volatile dword m_lock { 0 };
+ dword m_level { 0 };
+ int m_holder { -1 };
+};
+
+class GLocker {
+public:
+ [[gnu::always_inline]] inline explicit GLocker(GLock& l) : m_lock(l) { lock(); }
+ [[gnu::always_inline]] inline ~GLocker() { unlock(); }
+ [[gnu::always_inline]] inline void unlock() { m_lock.unlock(); }
+ [[gnu::always_inline]] inline void lock() { m_lock.lock(); }
+
+private:
+ GLock& m_lock;
+};
+
+[[gnu::always_inline]] inline void GLock::lock()
+{
+ for (;;) {
+ if (CAS(&m_lock, 1, 0) == 0) {
+ if (m_holder == -1 || m_holder == gettid()) {
+ m_holder = gettid();
+ ++m_level;
+ memory_barrier();
+ m_lock = 0;
+ return;
+ }
+ m_lock = 0;
+ }
+ dbgprintf("donate to %d\n", m_holder);
+ int rc = donate(m_holder);
+ if (rc < 0)
+ dbgprintf("donate: %s\n", strerror(errno));
+ }
+}
+
+inline void GLock::unlock()
+{
+ for (;;) {
+ if (CAS(&m_lock, 1, 0) == 0) {
+ ASSERT(m_holder == gettid());
+ ASSERT(m_level);
+ --m_level;
+ if (m_level) {
+ memory_barrier();
+ m_lock = 0;
+ return;
+ }
+ m_holder = -1;
+ memory_barrier();
+ m_lock = 0;
+ return;
+ }
+ donate(m_holder);
+ }
+}
+
+#define LOCKER(lock) GLocker locker(lock)
+
+template<typename T>
+class GLockable {
+public:
+ GLockable() { }
+ GLockable(T&& resource) : m_resource(move(resource)) { }
+ GLock& lock() { return m_lock; }
+ T& resource() { return m_resource; }
+
+ T lock_and_copy()
+ {
+ LOCKER(m_lock);
+ return m_resource;
+ }
+
+private:
+ T m_resource;
+ GLock m_lock;
+};
+