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.
This commit is contained in:
Andreas Kling 2019-03-25 13:03:49 +01:00
parent 108b663618
commit 500df578fe
Notes: sideshowbarker 2024-07-19 14:56:50 +09:00
10 changed files with 176 additions and 12 deletions

View file

@ -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) LOCKER(thumbnail_cache().lock());
continue; for (auto& it : thumbnail_cache().resource()) {
to_generate.append(it.key); 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); LOCKER(thumbnail_cache().lock());
if (it != thumbnail_cache().end()) { 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)

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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 {

View file

@ -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

View file

@ -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 :^)",
}; };

View file

@ -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);
}
} }

View file

@ -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
LibGUI/GLock.h Normal file
View file

@ -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;
};