Przeglądaj źródła

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.
Andreas Kling 6 lat temu
rodzic
commit
500df578fe

+ 21 - 11
Applications/FileManager/DirectoryModel.cpp

@@ -8,12 +8,13 @@
 #include <AK/StringBuilder.h>
 #include <AK/StringBuilder.h>
 #include <SharedGraphics/GraphicsBitmap.h>
 #include <SharedGraphics/GraphicsBitmap.h>
 #include <SharedGraphics/Painter.h>
 #include <SharedGraphics/Painter.h>
+#include <LibGUI/GLock.h>
 
 
-static HashMap<String, RetainPtr<GraphicsBitmap>>& thumbnail_cache()
+static GLockable<HashMap<String, RetainPtr<GraphicsBitmap>>>& thumbnail_cache()
 {
 {
-    static HashMap<String, RetainPtr<GraphicsBitmap>>* s_map;
+    static GLockable<HashMap<String, RetainPtr<GraphicsBitmap>>>* s_map;
     if (!s_map)
     if (!s_map)
-        s_map = new HashMap<String, RetainPtr<GraphicsBitmap>>();
+        s_map = new GLockable<HashMap<String, RetainPtr<GraphicsBitmap>>>();
     return *s_map;
     return *s_map;
 }
 }
 
 
@@ -23,11 +24,16 @@ int thumbnail_thread(void* model_ptr)
     for (;;) {
     for (;;) {
         sleep(1);
         sleep(1);
         Vector<String> to_generate;
         Vector<String> to_generate;
-        for (auto& it : thumbnail_cache()) {
-            if (it.value)
-                continue;
-            to_generate.append(it.key);
+        {
+            LOCKER(thumbnail_cache().lock());
+            for (auto& it : thumbnail_cache().resource()) {
+                if (it.value)
+                    continue;
+                to_generate.append(it.key);
+            }
         }
         }
+        if (to_generate.is_empty())
+            continue;
         for (int i = 0; i < to_generate.size(); ++i) {
         for (int i = 0; i < to_generate.size(); ++i) {
             auto& path = to_generate[i];
             auto& path = to_generate[i];
             auto png_bitmap = GraphicsBitmap::load_from_file(path);
             auto png_bitmap = GraphicsBitmap::load_from_file(path);
@@ -36,7 +42,10 @@ int thumbnail_thread(void* model_ptr)
             auto thumbnail = GraphicsBitmap::create(png_bitmap->format(), { 32, 32 });
             auto thumbnail = GraphicsBitmap::create(png_bitmap->format(), { 32, 32 });
             Painter painter(*thumbnail);
             Painter painter(*thumbnail);
             painter.draw_scaled_bitmap(thumbnail->rect(), *png_bitmap, png_bitmap->rect());
             painter.draw_scaled_bitmap(thumbnail->rect(), *png_bitmap, png_bitmap->rect());
-            thumbnail_cache().set(path, move(thumbnail));
+            {
+                LOCKER(thumbnail_cache().lock());
+                thumbnail_cache().resource().set(path, move(thumbnail));
+            }
             if (model.on_thumbnail_progress)
             if (model.on_thumbnail_progress)
                 model.on_thumbnail_progress(i + 1, to_generate.size());
                 model.on_thumbnail_progress(i + 1, to_generate.size());
             model.did_update();
             model.did_update();
@@ -121,11 +130,12 @@ GIcon DirectoryModel::icon_for(const Entry& entry) const
     if (entry.name.to_lowercase().ends_with(".png")) {
     if (entry.name.to_lowercase().ends_with(".png")) {
         if (!entry.thumbnail) {
         if (!entry.thumbnail) {
             auto path = entry.full_path(*this);
             auto path = entry.full_path(*this);
-            auto it = thumbnail_cache().find(path);
-            if (it != thumbnail_cache().end()) {
+            LOCKER(thumbnail_cache().lock());
+            auto it = thumbnail_cache().resource().find(path);
+            if (it != thumbnail_cache().resource().end()) {
                 entry.thumbnail = (*it).value.copy_ref();
                 entry.thumbnail = (*it).value.copy_ref();
             } else {
             } else {
-                thumbnail_cache().set(path, nullptr);
+                thumbnail_cache().resource().set(path, nullptr);
             }
             }
         }
         }
         if (!entry.thumbnail)
         if (!entry.thumbnail)

+ 24 - 0
Kernel/Process.cpp

@@ -2461,3 +2461,27 @@ int Process::sys$create_thread(int(*entry)(void*), void* argument)
     thread->set_state(Thread::State::Runnable);
     thread->set_state(Thread::State::Runnable);
     return 0;
     return 0;
 }
 }
+
+int Process::sys$gettid()
+{
+    return current->tid();
+}
+
+int Process::sys$donate(int tid)
+{
+    if (tid < 0)
+        return -EINVAL;
+    InterruptDisabler disabler;
+    Thread* beneficiary = nullptr;
+    for_each_thread([&] (Thread& thread) {
+        if (thread.tid() == tid) {
+            beneficiary = &thread;
+            return IterationDecision::Abort;
+        }
+        return IterationDecision::Continue;
+    });
+    if (!beneficiary)
+        return -ENOTHREAD;
+    Scheduler::donate_to(beneficiary, "sys$donate");
+    return 0;
+}

+ 2 - 0
Kernel/Process.h

@@ -97,6 +97,8 @@ public:
     void die();
     void die();
     void finalize();
     void finalize();
 
 
+    int sys$gettid();
+    int sys$donate(int tid);
     pid_t sys$setsid();
     pid_t sys$setsid();
     pid_t sys$getsid(pid_t);
     pid_t sys$getsid(pid_t);
     int sys$setpgid(pid_t pid, pid_t pgid);
     int sys$setpgid(pid_t pid, pid_t pgid);

+ 4 - 0
Kernel/Syscall.cpp

@@ -57,6 +57,10 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
     case Syscall::SC_yield:
     case Syscall::SC_yield:
         Scheduler::yield();
         Scheduler::yield();
         break;
         break;
+    case Syscall::SC_donate:
+        return current->process().sys$donate((int)arg1);
+    case Syscall::SC_gettid:
+        return current->process().sys$gettid();
     case Syscall::SC_putch:
     case Syscall::SC_putch:
         Console::the().put_char(arg1 & 0xff);
         Console::the().put_char(arg1 & 0xff);
         break;
         break;

+ 2 - 0
Kernel/Syscall.h

@@ -93,6 +93,8 @@
     __ENUMERATE_SYSCALL(getsockopt) \
     __ENUMERATE_SYSCALL(getsockopt) \
     __ENUMERATE_SYSCALL(setsockopt) \
     __ENUMERATE_SYSCALL(setsockopt) \
     __ENUMERATE_SYSCALL(create_thread) \
     __ENUMERATE_SYSCALL(create_thread) \
+    __ENUMERATE_SYSCALL(gettid) \
+    __ENUMERATE_SYSCALL(donate) \
 
 
 
 
 namespace Syscall {
 namespace Syscall {

+ 2 - 1
LibC/errno_numbers.h

@@ -70,4 +70,5 @@
 #define ETIMEDOUT       67
 #define ETIMEDOUT       67
 #define EPROTOTYPE      68
 #define EPROTOTYPE      68
 #define EINPROGRESS     69
 #define EINPROGRESS     69
-#define EMAXERRNO       70
+#define ENOTHREAD       70
+#define EMAXERRNO       71

+ 1 - 0
LibC/string.cpp

@@ -312,6 +312,7 @@ const char* sys_errlist[] = {
     "Timed out",
     "Timed out",
     "Wrong protocol type",
     "Wrong protocol type",
     "Operation in progress",
     "Operation in progress",
+    "No such thread",
     "The highest errno +1 :^)",
     "The highest errno +1 :^)",
 };
 };
 
 

+ 12 - 0
LibC/unistd.cpp

@@ -421,4 +421,16 @@ int ftruncate(int fd, off_t length)
     ASSERT_NOT_REACHED();
     ASSERT_NOT_REACHED();
 }
 }
 
 
+int gettid()
+{
+    int rc = syscall(SC_gettid);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
+int donate(int tid)
+{
+    int rc = syscall(SC_donate, tid);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
 }
 }

+ 2 - 0
LibC/unistd.h

@@ -14,6 +14,8 @@ __BEGIN_DECLS
 
 
 extern char** environ;
 extern char** environ;
 
 
+int gettid();
+int donate(int tid);
 int create_thread(int(*)(void*), void*);
 int create_thread(int(*)(void*), void*);
 int create_shared_buffer(pid_t peer_pid, int, void** buffer);
 int create_shared_buffer(pid_t peer_pid, int, void** buffer);
 void* get_shared_buffer(int shared_buffer_id);
 void* get_shared_buffer(int shared_buffer_id);

+ 106 - 0
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;
+};
+