diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 21b290a4..c9ed1946 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -90,6 +90,7 @@ SET(KERNEL_SRCS tasking/JoinBlocker.cpp tasking/BooleanBlocker.cpp tasking/PollBlocker.cpp + tasking/Futex.cpp tasking/SleepBlocker.cpp tasking/FileBlockers.cpp tasking/Tracer.cpp @@ -153,6 +154,7 @@ SET(KERNEL_SRCS syscall/exec.cpp syscall/exit.cpp syscall/fork.cpp + syscall/futex.cpp syscall/getcwd.cpp syscall/gettimeofday.cpp syscall/ioctl.cpp diff --git a/kernel/api/futex.h b/kernel/api/futex.h new file mode 100644 index 00000000..6a330cd0 --- /dev/null +++ b/kernel/api/futex.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once +#include "types.h" + +#define FUTEX_INIT 1 +#define FUTEX_DESTROY 2 +#define FUTEX_WAIT 3 + +__DECL_BEGIN + +typedef int futex_t; + +__DECL_END \ No newline at end of file diff --git a/kernel/syscall/futex.cpp b/kernel/syscall/futex.cpp new file mode 100644 index 00000000..0fac9f3c --- /dev/null +++ b/kernel/syscall/futex.cpp @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "../tasking/Process.h" +#include "../kernel/api/futex.h" +#include "../kernel/memory/SafePointer.h" + +int Process::sys_futex(UserspacePointer futex, int op) { + auto addr = (uintptr_t) futex.raw(); + if (addr > HIGHER_HALF) + return -EFAULT; + auto reg_res = _vm_space->get_region_containing(addr); + if (reg_res.is_error()) + return -EFAULT; + auto reg = reg_res.value(); + if (!reg->prot().read || !reg->prot().write) + return -EPERM; + + switch (op) { + case FUTEX_INIT: { + LOCK(m_futex_lock); + if (m_futexes.contains(addr)) + return -EEXIST; + m_futexes[addr] = kstd::Arc(new Futex(reg->object(), addr - reg->start())); + return SUCCESS; + } + case FUTEX_DESTROY: { + LOCK(m_futex_lock); + if (!m_futexes.contains(addr)) + return -ENOENT; + m_futexes.erase(addr); + } + case FUTEX_WAIT: { + auto k_futex = m_futex_lock.synced>([this, addr]() { + auto node = m_futexes.find_node(addr); + if (!node) + return kstd::Arc(); + return node->data.second; + }); + if (!k_futex) + return -ENOENT; + TaskManager::current_thread()->block(*k_futex); + return SUCCESS; + } + default: + return -EINVAL; + } +} \ No newline at end of file diff --git a/kernel/syscall/syscall.cpp b/kernel/syscall/syscall.cpp index 4557c7e1..69ac22a2 100644 --- a/kernel/syscall/syscall.cpp +++ b/kernel/syscall/syscall.cpp @@ -209,6 +209,8 @@ int handle_syscall(ThreadRegisters& regs, uint32_t call, uint32_t arg1, uint32_t return cur_proc->sys_shutdown(arg1, arg2); case SYS_ACCEPT: return cur_proc->sys_accept(arg1, (struct sockaddr*) arg2, (uint32_t*) arg3); + case SYS_FUTEX: + return cur_proc->sys_futex((int*) arg1, arg2); //TODO: Implement these syscalls case SYS_TIMES: diff --git a/kernel/syscall/syscall_numbers.h b/kernel/syscall/syscall_numbers.h index d93a4ddd..26f451ec 100644 --- a/kernel/syscall/syscall_numbers.h +++ b/kernel/syscall/syscall_numbers.h @@ -90,6 +90,7 @@ #define SYS_LISTEN 87 #define SYS_SHUTDOWN 88 #define SYS_ACCEPT 89 +#define SYS_FUTEX 90 #ifndef DUCKOS_KERNEL #include diff --git a/kernel/tasking/Futex.cpp b/kernel/tasking/Futex.cpp new file mode 100644 index 00000000..cff1ced8 --- /dev/null +++ b/kernel/tasking/Futex.cpp @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "Futex.h" +#include "../memory/MemoryManager.h" + +Futex::Futex(kstd::Arc object, size_t offset_in_object): + m_object(kstd::move(object)), + m_k_region(MM.map_object(m_object)), + m_var((Atomic*) (m_k_region->start() + offset_in_object)) +{ + ASSERT(offset_in_object + sizeof(*m_var) <= m_object->size()); +} + +bool Futex::is_ready() { + return m_var->load(MemoryOrder::Relaxed) > 0; +} \ No newline at end of file diff --git a/kernel/tasking/Futex.h b/kernel/tasking/Futex.h new file mode 100644 index 00000000..96317815 --- /dev/null +++ b/kernel/tasking/Futex.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "Blocker.h" +#include "../memory/VMRegion.h" + +class Futex: public Blocker { +public: + Futex(kstd::Arc object, size_t offset_in_object); + + bool is_ready() override; + +private: + kstd::Arc m_object; + kstd::Arc m_k_region; + Atomic* m_var; +}; diff --git a/kernel/tasking/Process.h b/kernel/tasking/Process.h index 106729b4..425554e3 100644 --- a/kernel/tasking/Process.h +++ b/kernel/tasking/Process.h @@ -30,6 +30,7 @@ #include "../api/mmap.h" #include "Tracer.h" #include "../kstd/KLog.h" +#include "Futex.h" class FileDescriptor; class Blocker; @@ -185,6 +186,7 @@ class Process { int sys_listen(int sockfd, int backlog); int sys_shutdown(int sockfd, int how); int sys_accept(int sockfd, UserspacePointer addr, UserspacePointer addrlen); + int sys_futex(UserspacePointer futex, int operation); private: friend class Thread; @@ -243,6 +245,8 @@ class Process { Mutex m_fd_lock { "Process::FileDescriptor" }; kstd::vector> _file_descriptors; kstd::Arc _cwd; + Mutex m_futex_lock { "Process::Futexes" }; + kstd::map> m_futexes; //Signals Signal::SigAction signal_actions[32] = {{Signal::SigAction()}}; diff --git a/libraries/libc/CMakeLists.txt b/libraries/libc/CMakeLists.txt index f2bc0d52..13b3d8b4 100644 --- a/libraries/libc/CMakeLists.txt +++ b/libraries/libc/CMakeLists.txt @@ -22,6 +22,7 @@ SET(SOURCES strings.c sys/ioctl.c sys/shm.c + sys/futex.c sys/printf.c sys/ptrace.c sys/liballoc.cpp diff --git a/libraries/libc/sys/futex.c b/libraries/libc/sys/futex.c new file mode 100644 index 00000000..8ef3c277 --- /dev/null +++ b/libraries/libc/sys/futex.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "futex.h" +#include "syscall.h" + +int futex_init(futex_t* futex, int val) { + *futex = val; + return syscall3(SYS_FUTEX, (int) futex, FUTEX_INIT); +} + +int futex_destroy(futex_t* futex) { + return syscall3(SYS_FUTEX, (int) futex, FUTEX_DESTROY); +} + +void futex_wait(futex_t* futex) { + int exp = __atomic_load_n(futex, __ATOMIC_RELAXED); + while (1) { + if (exp > 0) { + if (__atomic_compare_exchange_n(futex, &exp, exp - 1, 0, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + break; + } else { + if (syscall3_noerr(SYS_FUTEX, (int) futex, FUTEX_WAIT)) + return; + exp = __atomic_load_n(futex, __ATOMIC_RELAXED); + } + } +} + +void futex_signal(futex_t* futex) { + __atomic_fetch_add(futex, 1, __ATOMIC_ACQUIRE); +} \ No newline at end of file diff --git a/libraries/libc/sys/futex.h b/libraries/libc/sys/futex.h new file mode 100644 index 00000000..c0c8ed0a --- /dev/null +++ b/libraries/libc/sys/futex.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once +#include + +__DECL_BEGIN + +/** + * Initializes a futex. + * @param futex Pointer to the futex_t to initialize. + * @param val The value to initialize the futex with. + * @return 0 on success, -1 on error (errno set). + */ +int futex_init(futex_t* futex, int val); + +/** + * Destroys a futex. + * @param futex Pointer to the futex to destroy. + * @return 0 on success, -1 on error (errno set). + */ +int futex_destroy(futex_t* futex); + +/** + * Waits for a futex to be greater than zero and then subtracts one from its stored value. + * @param futex Pointer to the futex to wait on. + */ +void futex_wait(futex_t* futex); + +/** + * Adds one to the futex's stored value. + * @param futex Poitner to the futex to signal. + */ +void futex_signal(futex_t* futex); + +__DECL_END \ No newline at end of file