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
This commit is contained in:
Bananymous 2026-01-13 19:18:52 +02:00
parent c30fc9d60f
commit a83fa6f4c6
2 changed files with 36 additions and 19 deletions

View File

@ -348,6 +348,7 @@ namespace Kernel
struct futex_t struct futex_t
{ {
Mutex mutex;
ThreadBlocker blocker; ThreadBlocker blocker;
uint32_t waiters { 0 }; uint32_t waiters { 0 };
uint32_t to_wakeup { 0 }; uint32_t to_wakeup { 0 };

View File

@ -3142,10 +3142,31 @@ namespace Kernel
return SystemTimer::get().ns_since_boot() + (abs_ns - real_ns); return SystemTimer::get().ns_since_boot() + (abs_ns - real_ns);
}()); }());
auto& futex_lock = is_private ? m_futex_lock : s_futex_lock; if (op == FUTEX_WAIT && BAN::atomic_load(*addr) != val)
auto& futexes = is_private ? m_futexes : s_futexes; 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<futex_t>::create())))->value.ptr();
break;
case FUTEX_WAKE:
return 0;
}
}
LockGuard _(futex->mutex);
switch (op) switch (op)
{ {
@ -3154,20 +3175,17 @@ namespace Kernel
if (BAN::atomic_load(*addr) != val) if (BAN::atomic_load(*addr) != val)
return BAN::Error::from_errno(EAGAIN); return BAN::Error::from_errno(EAGAIN);
auto it = futexes.find(paddr);
if (it == futexes.end())
it = TRY(futexes.emplace(paddr, TRY(BAN::UniqPtr<futex_t>::create())));
futex_t* const futex = it->value.ptr();
futex->waiters++; futex->waiters++;
BAN::ScopeGuard _([futex] {
// TODO: Lazily deallocate unused futex objects at some point (?) // TODO: Deallocate unused futex objects at some point (?)
futex->waiters--; // 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 (;;) 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) if (BAN::atomic_load(*addr) == val || futex->to_wakeup == 0)
continue; continue;
futex->to_wakeup--; futex->to_wakeup--;
@ -3176,18 +3194,16 @@ namespace Kernel
} }
case FUTEX_WAKE: 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)) if (BAN::Math::will_addition_overflow(futex->to_wakeup, val))
futex->to_wakeup = BAN::numeric_limits<uint32_t>::max(); futex->to_wakeup = BAN::numeric_limits<uint32_t>::max();
else else
futex->to_wakeup += val; futex->to_wakeup += val;
futex->to_wakeup = BAN::Math::min(futex->to_wakeup, futex->waiters); futex->to_wakeup = BAN::Math::min(futex->to_wakeup, futex->waiters);
futex->blocker.unblock();
if (futex->to_wakeup > 0)
futex->blocker.unblock();
return 0; return 0;
} }
} }