Skip to content

Commit

Permalink
Kernel/libc: Allow for futex file descriptors
Browse files Browse the repository at this point in the history
This way, we can use poll() with futexes. Using poll() with a futex doesn't actually wait on it, but rather unblocks when it is ready to be waited on - so this is inherently racy and should be used with caution.
  • Loading branch information
byteduck committed Apr 19, 2024
1 parent 82f7b0d commit e1f643c
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 52 deletions.
5 changes: 2 additions & 3 deletions kernel/api/futex.h
Expand Up @@ -4,9 +4,8 @@
#pragma once
#include "types.h"

#define FUTEX_INIT 1
#define FUTEX_DESTROY 2
#define FUTEX_WAIT 3
#define FUTEX_WAIT 1
#define FUTEX_REGFD 2

__DECL_BEGIN

Expand Down
39 changes: 15 additions & 24 deletions kernel/syscall/futex.cpp
Expand Up @@ -2,8 +2,10 @@
/* Copyright © 2016-2024 Byteduck */

#include "../tasking/Process.h"
#include "../kernel/api/futex.h"
#include "../kernel/memory/SafePointer.h"
#include "../api/futex.h"
#include "../memory/SafePointer.h"
#include "../filesystem/FileDescriptor.h"
#include "../tasking/Futex.h"

int Process::sys_futex(UserspacePointer<futex_t> futex, int op) {
auto addr = (uintptr_t) futex.raw();
Expand All @@ -17,29 +19,18 @@ int Process::sys_futex(UserspacePointer<futex_t> futex, int op) {
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;
case FUTEX_REGFD:
return m_fd_lock.synced<int>([this, addr, reg] {
auto futex = kstd::Arc(new Futex(reg->object(), addr - reg->start()));
auto fd = kstd::Arc(new FileDescriptor(futex, this));
_file_descriptors.push_back(fd);
fd->set_id((int) _file_descriptors.size() - 1);
return (int)_file_descriptors.size() - 1;
});
if (!k_futex)
return -ENOENT;
TaskManager::current_thread()->block(*k_futex);
case FUTEX_WAIT: {
printf("Wait...\n");
Futex k_futex {reg->object(), addr - reg->start()};
TaskManager::current_thread()->block(k_futex);
return SUCCESS;
}
default:
Expand Down
6 changes: 5 additions & 1 deletion kernel/tasking/Futex.cpp
Expand Up @@ -14,4 +14,8 @@ Futex::Futex(kstd::Arc<VMObject> object, size_t offset_in_object):

bool Futex::is_ready() {
return m_var->load(MemoryOrder::Relaxed) > 0;
}
}

bool Futex::can_read(const FileDescriptor& fd) {
return is_ready();
}
7 changes: 6 additions & 1 deletion kernel/tasking/Futex.h
Expand Up @@ -5,13 +5,18 @@

#include "Blocker.h"
#include "../memory/VMRegion.h"
#include "../filesystem/File.h"

class Futex: public Blocker {
class Futex: public Blocker, public File {
public:
Futex(kstd::Arc<VMObject> object, size_t offset_in_object);

// Blocker
bool is_ready() override;

// File
bool can_read(const FileDescriptor& fd) override;

private:
kstd::Arc<VMObject> m_object;
kstd::Arc<VMRegion> m_k_region;
Expand Down
3 changes: 0 additions & 3 deletions kernel/tasking/Process.h
Expand Up @@ -30,7 +30,6 @@
#include "../api/mmap.h"
#include "Tracer.h"
#include "../kstd/KLog.h"
#include "Futex.h"

class FileDescriptor;
class Blocker;
Expand Down Expand Up @@ -245,8 +244,6 @@ 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/dlfcn.h
Expand Up @@ -12,6 +12,7 @@ __DECL_BEGIN
#define RTLD_LAZY 2
#define RTLD_NOW 4
#define RTLD_LOCAL 8
#define RTLD_GLOBAL 16

void* dlopen(const char*, int);
int dlclose(void*);
Expand Down
25 changes: 16 additions & 9 deletions libraries/libc/sys/futex.c
Expand Up @@ -4,13 +4,8 @@
#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);
int futex_open(futex_t* futex) {
return syscall3(SYS_FUTEX, (int) futex, FUTEX_REGFD);
}

void futex_wait(futex_t* futex) {
Expand All @@ -20,13 +15,25 @@ void futex_wait(futex_t* futex) {
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;
syscall3_noerr(SYS_FUTEX, (int) futex, FUTEX_WAIT);
exp = __atomic_load_n(futex, __ATOMIC_RELAXED);
}
}
}

int futex_trywait(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 {
return 0;
}
}
return 1;
}

void futex_signal(futex_t* futex) {
__atomic_fetch_add(futex, 1, __ATOMIC_ACQUIRE);
}
21 changes: 10 additions & 11 deletions libraries/libc/sys/futex.h
Expand Up @@ -7,26 +7,25 @@
__DECL_BEGIN

/**
* Initializes a futex.
* Registers a futex to a file descriptor, so that it can be waited on using poll().
* @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).
* @return file descriptor 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);
int futex_open(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);

/**
* Tries to wait for a futex to be greater than zero and subtracts one from its stored value if successful.
* @param futex Pointer to the futex to try waiting on.
* @return 1 if the futex was waited on, 0 if not.
*/
int futex_trywait(futex_t* futex);

/**
* Adds one to the futex's stored value.
* @param futex Poitner to the futex to signal.
Expand Down

0 comments on commit e1f643c

Please sign in to comment.