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_sigpending(sigset_t* set);
|
||||||
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
|
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_yield();
|
||||||
BAN::ErrorOr<long> sys_set_tls(void*);
|
BAN::ErrorOr<long> sys_set_tls(void*);
|
||||||
BAN::ErrorOr<long> sys_get_tls();
|
BAN::ErrorOr<long> sys_get_tls();
|
||||||
|
@ -337,6 +338,14 @@ namespace Kernel
|
||||||
BAN::UniqPtr<PageTable> m_page_table;
|
BAN::UniqPtr<PageTable> m_page_table;
|
||||||
BAN::RefPtr<TTY> m_controlling_terminal;
|
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;
|
friend class Thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/banan-os.h>
|
#include <sys/banan-os.h>
|
||||||
|
#include <sys/futex.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
@ -2557,6 +2558,96 @@ namespace Kernel
|
||||||
return 0;
|
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()
|
BAN::ErrorOr<long> Process::sys_yield()
|
||||||
{
|
{
|
||||||
Processor::yield();
|
Processor::yield();
|
||||||
|
|
|
@ -36,6 +36,7 @@ set(LIBC_SOURCES
|
||||||
sys/banan-os.cpp
|
sys/banan-os.cpp
|
||||||
sys/epoll.cpp
|
sys/epoll.cpp
|
||||||
sys/file.cpp
|
sys/file.cpp
|
||||||
|
sys/futex.cpp
|
||||||
sys/ioctl.cpp
|
sys/ioctl.cpp
|
||||||
sys/mman.cpp
|
sys/mman.cpp
|
||||||
sys/resource.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_EPOLL_PWAIT2, epoll_pwait2) \
|
||||||
O(SYS_FLOCK, flock) \
|
O(SYS_FLOCK, flock) \
|
||||||
O(SYS_GET_NPROCESSOR, get_nprocessor) \
|
O(SYS_GET_NPROCESSOR, get_nprocessor) \
|
||||||
|
O(SYS_FUTEX, futex) \
|
||||||
|
|
||||||
enum Syscall
|
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