diff --git a/Applications/FileManager/DirectoryModel.cpp b/Applications/FileManager/DirectoryModel.cpp index 6cb65b913da..f4221ee5168 100644 --- a/Applications/FileManager/DirectoryModel.cpp +++ b/Applications/FileManager/DirectoryModel.cpp @@ -8,12 +8,13 @@ #include #include #include +#include -static HashMap>& thumbnail_cache() +static GLockable>>& thumbnail_cache() { - static HashMap>* s_map; + static GLockable>>* s_map; if (!s_map) - s_map = new HashMap>(); + s_map = new GLockable>>(); return *s_map; } @@ -23,11 +24,16 @@ int thumbnail_thread(void* model_ptr) for (;;) { sleep(1); Vector 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) { auto& path = to_generate[i]; 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 }); Painter painter(*thumbnail); 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) model.on_thumbnail_progress(i + 1, to_generate.size()); 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.thumbnail) { 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(); } else { - thumbnail_cache().set(path, nullptr); + thumbnail_cache().resource().set(path, nullptr); } } if (!entry.thumbnail) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 363487ebd5a..c7b009aeb7f 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2461,3 +2461,27 @@ int Process::sys$create_thread(int(*entry)(void*), void* argument) thread->set_state(Thread::State::Runnable); 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; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 66d0770c419..cc15c1c2511 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -97,6 +97,8 @@ public: void die(); void finalize(); + int sys$gettid(); + int sys$donate(int tid); pid_t sys$setsid(); pid_t sys$getsid(pid_t); int sys$setpgid(pid_t pid, pid_t pgid); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index fcc5845787a..abce43a6c2e 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -57,6 +57,10 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, case Syscall::SC_yield: Scheduler::yield(); 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: Console::the().put_char(arg1 & 0xff); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 8124cbc6949..6bb0e3ed051 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -93,6 +93,8 @@ __ENUMERATE_SYSCALL(getsockopt) \ __ENUMERATE_SYSCALL(setsockopt) \ __ENUMERATE_SYSCALL(create_thread) \ + __ENUMERATE_SYSCALL(gettid) \ + __ENUMERATE_SYSCALL(donate) \ namespace Syscall { diff --git a/LibC/errno_numbers.h b/LibC/errno_numbers.h index 710982f2052..2527d44e97d 100644 --- a/LibC/errno_numbers.h +++ b/LibC/errno_numbers.h @@ -70,4 +70,5 @@ #define ETIMEDOUT 67 #define EPROTOTYPE 68 #define EINPROGRESS 69 -#define EMAXERRNO 70 +#define ENOTHREAD 70 +#define EMAXERRNO 71 diff --git a/LibC/string.cpp b/LibC/string.cpp index 02a5dba3f57..eed302db5f8 100644 --- a/LibC/string.cpp +++ b/LibC/string.cpp @@ -312,6 +312,7 @@ const char* sys_errlist[] = { "Timed out", "Wrong protocol type", "Operation in progress", + "No such thread", "The highest errno +1 :^)", }; diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index 9082d5d92a0..6d820d66463 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -421,4 +421,16 @@ int ftruncate(int fd, off_t length) 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); +} + } diff --git a/LibC/unistd.h b/LibC/unistd.h index 5f38e15b638..9114572fdcd 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -14,6 +14,8 @@ __BEGIN_DECLS extern char** environ; +int gettid(); +int donate(int tid); int create_thread(int(*)(void*), void*); int create_shared_buffer(pid_t peer_pid, int, void** buffer); void* get_shared_buffer(int shared_buffer_id); diff --git a/LibGUI/GLock.h b/LibGUI/GLock.h new file mode 100644 index 00000000000..7180ecca2ad --- /dev/null +++ b/LibGUI/GLock.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include + +#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 +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; +}; +