Kernel/LibC: Implement simple futex
This commit is contained in:
parent
658a001d91
commit
5940e912b3
|
@ -189,6 +189,7 @@ namespace Kernel
|
|||
BAN::ErrorOr<long> sys_sigpending(sigset_t* set);
|
||||
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
|
||||
|
||||
BAN::ErrorOr<long> sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime);
|
||||
BAN::ErrorOr<long> sys_yield();
|
||||
BAN::ErrorOr<long> sys_set_tls(void*);
|
||||
BAN::ErrorOr<long> sys_get_tls();
|
||||
|
@ -337,6 +338,14 @@ namespace Kernel
|
|||
BAN::UniqPtr<PageTable> m_page_table;
|
||||
BAN::RefPtr<TTY> m_controlling_terminal;
|
||||
|
||||
struct futex_t
|
||||
{
|
||||
ThreadBlocker blocker;
|
||||
uint32_t waiters { 0 };
|
||||
uint32_t to_wakeup { 0 };
|
||||
};
|
||||
BAN::HashMap<paddr_t, BAN::UniqPtr<futex_t>> m_futexes;
|
||||
|
||||
friend class Thread;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/futex.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
@ -2557,6 +2558,96 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime)
|
||||
{
|
||||
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr);
|
||||
if (vaddr % 4)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
const bool is_private = (op & FUTEX_PRIVATE);
|
||||
const bool is_realtime = (op & FUTEX_REALTIME);
|
||||
op &= ~(FUTEX_PRIVATE | FUTEX_REALTIME);
|
||||
|
||||
if (!is_private)
|
||||
{
|
||||
dwarnln("TODO: shared futex");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
LockGuard _(m_process_lock);
|
||||
|
||||
auto* buffer_region = TRY(validate_and_pin_pointer_access(addr, sizeof(uint32_t), false));
|
||||
BAN::ScopeGuard pin_guard([&] { if (buffer_region) buffer_region->unpin(); });
|
||||
|
||||
const paddr_t paddr = m_page_table->physical_address_of(vaddr & PAGE_ADDR_MASK) | (vaddr & ~PAGE_ADDR_MASK);
|
||||
ASSERT(paddr != 0);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case FUTEX_WAIT:
|
||||
{
|
||||
if (BAN::atomic_load(*addr) != val)
|
||||
return BAN::Error::from_errno(EAGAIN);
|
||||
|
||||
const uint64_t wake_time_ns =
|
||||
TRY([abstime, is_realtime, this]() -> BAN::ErrorOr<uint64_t>
|
||||
{
|
||||
if (abstime == nullptr)
|
||||
return BAN::numeric_limits<uint64_t>::max();
|
||||
TRY(validate_pointer_access(abstime, sizeof(*abstime), false));
|
||||
const uint64_t abs_ns = abstime->tv_sec * 1'000'000'000 + abstime->tv_nsec;
|
||||
if (!is_realtime)
|
||||
return abs_ns;
|
||||
const auto realtime = SystemTimer::get().real_time();
|
||||
const uint64_t real_ns = realtime.tv_sec * 1'000'000'000 + realtime.tv_nsec;
|
||||
if (abs_ns <= real_ns)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
return SystemTimer::get().ns_since_boot() + (abs_ns - real_ns);
|
||||
}());
|
||||
|
||||
auto it = m_futexes.find(paddr);
|
||||
if (it == m_futexes.end())
|
||||
it = TRY(m_futexes.emplace(paddr, TRY(BAN::UniqPtr<futex_t>::create())));
|
||||
futex_t* const futex = it->value.ptr();
|
||||
|
||||
futex->waiters++;
|
||||
BAN::ScopeGuard _([futex, paddr, this] {
|
||||
if (--futex->waiters == 0)
|
||||
m_futexes.remove(paddr);
|
||||
});
|
||||
|
||||
for (;;)
|
||||
{
|
||||
TRY(Thread::current().block_or_eintr_or_waketime_ns(futex->blocker, wake_time_ns, true, &m_process_lock));
|
||||
if (BAN::atomic_load(*addr) == val || futex->to_wakeup == 0)
|
||||
continue;
|
||||
futex->to_wakeup--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case FUTEX_WAKE:
|
||||
{
|
||||
auto it = m_futexes.find(paddr);
|
||||
if (it == m_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<uint32_t>::max();
|
||||
else
|
||||
futex->to_wakeup += val;
|
||||
|
||||
futex->to_wakeup = BAN::Math::min(futex->to_wakeup, futex->waiters);
|
||||
futex->blocker.unblock();
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return BAN::Error::from_errno(ENOSYS);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_yield()
|
||||
{
|
||||
Processor::yield();
|
||||
|
|
|
@ -36,6 +36,7 @@ set(LIBC_SOURCES
|
|||
sys/banan-os.cpp
|
||||
sys/epoll.cpp
|
||||
sys/file.cpp
|
||||
sys/futex.cpp
|
||||
sys/ioctl.cpp
|
||||
sys/mman.cpp
|
||||
sys/resource.cpp
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef _SYS_FUTEX_H
|
||||
#define _SYS_FUTEX_H 1
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
#define FUTEX_PRIVATE 0x10
|
||||
#define FUTEX_REALTIME 0x20
|
||||
|
||||
#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE)
|
||||
#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE)
|
||||
|
||||
// op is one of FUTEX_WAIT or FUTEX_WAKE optionally or'ed with FUTEX_PRIVATE and/or FUTEX_REALTIME
|
||||
//
|
||||
// FUTEX_WAIT
|
||||
// put current thread to sleep until *addr != value or until timeout occurs
|
||||
// timeout is specified as a absolute time or NULL for indefinite wait
|
||||
//
|
||||
// FUTEX_WAKE
|
||||
// signals waiting futexes to recheck *addr. at most value threads are woken up
|
||||
//
|
||||
// FUTEX_PRIVATE
|
||||
// limit futex wait/wake events to the current process
|
||||
//
|
||||
// FUTEX_REALTIME
|
||||
// abstime corresponds to CLOCK_REALTIME instead of the default CLOCK_MONOTONIC
|
||||
//
|
||||
// ERRORS
|
||||
// ETIMEDOUT timeout occured
|
||||
// EINVAL addr is not aligned on 4 byte boundary
|
||||
// ENOSYS op contains unrecognized value
|
||||
// EINTR function was interrupted
|
||||
// ENOMEM not enough memory to allocate futex object
|
||||
// EAGAIN *addr != value before thread was put to sleep
|
||||
int futex(int op, const uint32_t* addr, uint32_t value, const struct timespec* abstime);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
|
@ -109,6 +109,7 @@ __BEGIN_DECLS
|
|||
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \
|
||||
O(SYS_FLOCK, flock) \
|
||||
O(SYS_GET_NPROCESSOR, get_nprocessor) \
|
||||
O(SYS_FUTEX, futex) \
|
||||
|
||||
enum Syscall
|
||||
{
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <sys/futex.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int futex(int op, const uint32_t* addr, uint32_t value, const struct timespec* abstime)
|
||||
{
|
||||
errno = 0;
|
||||
while (syscall(SYS_FUTEX, op, addr, value, abstime) == -1 && errno == EINTR)
|
||||
errno = 0;
|
||||
return errno;
|
||||
}
|
Loading…
Reference in New Issue