Skip to content

Commit

Permalink
Kernel/libc: New futex primitive
Browse files Browse the repository at this point in the history
Fast userspace mutex (behaves like a semaphore) which only requires a syscall in the case of contention.
  • Loading branch information
byteduck committed Apr 19, 2024
1 parent 40eeb35 commit 82f7b0d
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 0 deletions.
2 changes: 2 additions & 0 deletions kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions kernel/api/futex.h
Original file line number Diff line number Diff line change
@@ -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
48 changes: 48 additions & 0 deletions kernel/syscall/futex.cpp
Original file line number Diff line number Diff line change
@@ -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_t> 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<kstd::Arc<Futex>>([this, addr]() {
auto node = m_futexes.find_node(addr);
if (!node)
return kstd::Arc<Futex>();
return node->data.second;
});
if (!k_futex)
return -ENOENT;
TaskManager::current_thread()->block(*k_futex);
return SUCCESS;
}
default:
return -EINVAL;
}
}
2 changes: 2 additions & 0 deletions kernel/syscall/syscall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions kernel/syscall/syscall_numbers.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
#define SYS_LISTEN 87
#define SYS_SHUTDOWN 88
#define SYS_ACCEPT 89
#define SYS_FUTEX 90

#ifndef DUCKOS_KERNEL
#include <sys/types.h>
Expand Down
17 changes: 17 additions & 0 deletions kernel/tasking/Futex.cpp
Original file line number Diff line number Diff line change
@@ -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<VMObject> object, size_t offset_in_object):
m_object(kstd::move(object)),
m_k_region(MM.map_object(m_object)),
m_var((Atomic<int>*) (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;
}
19 changes: 19 additions & 0 deletions kernel/tasking/Futex.h
Original file line number Diff line number Diff line change
@@ -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<VMObject> object, size_t offset_in_object);

bool is_ready() override;

private:
kstd::Arc<VMObject> m_object;
kstd::Arc<VMRegion> m_k_region;
Atomic<int>* m_var;
};
4 changes: 4 additions & 0 deletions kernel/tasking/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "../api/mmap.h"
#include "Tracer.h"
#include "../kstd/KLog.h"
#include "Futex.h"

class FileDescriptor;
class Blocker;
Expand Down Expand Up @@ -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<struct sockaddr> addr, UserspacePointer<uint32_t> addrlen);
int sys_futex(UserspacePointer<int> futex, int operation);

private:
friend class Thread;
Expand Down Expand Up @@ -243,6 +245,8 @@ class Process {
Mutex m_fd_lock { "Process::FileDescriptor" };
kstd::vector<kstd::Arc<FileDescriptor>> _file_descriptors;
kstd::Arc<LinkedInode> _cwd;
Mutex m_futex_lock { "Process::Futexes" };
kstd::map<uintptr_t, kstd::Arc<Futex>> m_futexes;

//Signals
Signal::SigAction signal_actions[32] = {{Signal::SigAction()}};
Expand Down
1 change: 1 addition & 0 deletions libraries/libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions libraries/libc/sys/futex.c
Original file line number Diff line number Diff line change
@@ -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);
}
36 changes: 36 additions & 0 deletions libraries/libc/sys/futex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#pragma once
#include <kernel/api/futex.h>

__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

0 comments on commit 82f7b0d

Please sign in to comment.