diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-03-25 13:03:49 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-03-25 13:03:49 +0100 |
commit | 500df578fe5b24d14062d030c5628cbb4b69d3a6 (patch) | |
tree | e484af9a08e7463e95e947f8e8af3b94fb3d6645 /LibGUI | |
parent | 108b663618e893c804c150205a21f2e8d95088c2 (diff) | |
download | serenity-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.h | 106 |
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; +}; + |