From a83fa6f4c62cbc89ee307b552a68db2484aeb924 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 13 Jan 2026 19:18:52 +0200 Subject: [PATCH] Kernel: Optimize futexes Eeach futex object now has its own mutex to prevent unnecessary locking of the process/global futex lock. This basically removes sys_futex from profiles when running software with llvmpipe --- kernel/include/kernel/Process.h | 1 + kernel/kernel/Process.cpp | 54 +++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 1df6c4c5..9c166977 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -348,6 +348,7 @@ namespace Kernel struct futex_t { + Mutex mutex; ThreadBlocker blocker; uint32_t waiters { 0 }; uint32_t to_wakeup { 0 }; diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 7de519ca..46ca6d68 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -3142,10 +3142,31 @@ namespace Kernel return SystemTimer::get().ns_since_boot() + (abs_ns - real_ns); }()); - auto& futex_lock = is_private ? m_futex_lock : s_futex_lock; - auto& futexes = is_private ? m_futexes : s_futexes; + if (op == FUTEX_WAIT && BAN::atomic_load(*addr) != val) + return BAN::Error::from_errno(EAGAIN); - LockGuard _(futex_lock); + futex_t* futex; + + { + auto& futex_lock = is_private ? m_futex_lock : s_futex_lock; + auto& futexes = is_private ? m_futexes : s_futexes; + + LockGuard _(futex_lock); + + auto it = futexes.find(paddr); + if (it != futexes.end()) + futex = it->value.ptr(); + else switch (op) + { + case FUTEX_WAIT: + futex = TRY(futexes.emplace(paddr, TRY(BAN::UniqPtr::create())))->value.ptr(); + break; + case FUTEX_WAKE: + return 0; + } + } + + LockGuard _(futex->mutex); switch (op) { @@ -3154,20 +3175,17 @@ namespace Kernel if (BAN::atomic_load(*addr) != val) return BAN::Error::from_errno(EAGAIN); - auto it = futexes.find(paddr); - if (it == futexes.end()) - it = TRY(futexes.emplace(paddr, TRY(BAN::UniqPtr::create()))); - futex_t* const futex = it->value.ptr(); - futex->waiters++; - BAN::ScopeGuard _([futex] { - // TODO: Lazily deallocate unused futex objects at some point (?) - futex->waiters--; - }); + + // TODO: Deallocate unused futex objects at some point (?) + // We don't want to do it on every operation as allocation + // and deletion slows this down a lot and the same addresses + // will be probably used again + BAN::ScopeGuard cleanup([futex] { futex->waiters--; }); for (;;) { - TRY(Thread::current().block_or_eintr_or_waketime_ns(futex->blocker, wake_time_ns, true, &futex_lock)); + TRY(Thread::current().block_or_eintr_or_waketime_ns(futex->blocker, wake_time_ns, true, &futex->mutex)); if (BAN::atomic_load(*addr) == val || futex->to_wakeup == 0) continue; futex->to_wakeup--; @@ -3176,18 +3194,16 @@ namespace Kernel } case FUTEX_WAKE: { - auto it = futexes.find(paddr); - if (it == futexes.end()) - return 0; - futex_t* const futex = it->value.ptr(); - if (BAN::Math::will_addition_overflow(futex->to_wakeup, val)) futex->to_wakeup = BAN::numeric_limits::max(); else futex->to_wakeup += val; futex->to_wakeup = BAN::Math::min(futex->to_wakeup, futex->waiters); - futex->blocker.unblock(); + + if (futex->to_wakeup > 0) + futex->blocker.unblock(); + return 0; } }