PatchworkOS
Loading...
Searching...
No Matches
futex.c
Go to the documentation of this file.
2#include <kernel/log/log.h>
7#include <kernel/sched/wait.h>
8#include <kernel/sync/futex.h>
9#include <kernel/sync/lock.h>
10#include <kernel/utils/map.h>
11
12#include <errno.h>
13#include <stdlib.h>
14
16{
17 map_init(&ctx->futexes);
18 lock_init(&ctx->lock);
19}
20
22{
23 for (uint64_t i = 0; i < ctx->futexes.capacity; i++)
24 {
25 map_entry_t* entry = ctx->futexes.entries[i];
26 if (!MAP_ENTRY_PTR_IS_VALID(entry))
27 {
28 continue;
29 }
30
31 futex_t* futex = CONTAINER_OF(entry, futex_t, entry);
32 wait_queue_deinit(&futex->queue);
33 free(futex);
34 }
35 map_deinit(&ctx->futexes);
36}
37
38static futex_t* futex_ctx_get(futex_ctx_t* ctx, void* addr)
39{
40 LOCK_SCOPE(&ctx->lock);
41
43 futex_t* futex = CONTAINER_OF(map_get(&ctx->futexes, &key), futex_t, entry);
44 if (futex != NULL)
45 {
46 return futex;
47 }
48
49 futex = malloc(sizeof(futex_t));
50 if (futex == NULL)
51 {
52 return NULL;
53 }
54
55 map_entry_init(&futex->entry);
56 wait_queue_init(&futex->queue);
57
58 map_insert(&ctx->futexes, &key, &futex->entry);
59 return futex;
60}
61
62SYSCALL_DEFINE(SYS_FUTEX, uint64_t, atomic_uint64_t* addr, uint64_t val, futex_op_t op, clock_t timeout)
63{
64 thread_t* thread = sched_thread();
65 process_t* process = thread->process;
66 space_t* space = &process->space;
67 futex_ctx_t* ctx = &process->futexCtx;
68
69 futex_t* futex = futex_ctx_get(ctx, addr);
70 if (futex == NULL)
71 {
72 return ERR;
73 }
74
75 switch (op)
76 {
77 case FUTEX_WAIT:
78 {
80
81 clock_t deadline;
82 if (timeout == CLOCKS_NEVER)
83 {
84 deadline = CLOCKS_NEVER;
85 }
86 else if (timeout > CLOCKS_NEVER - uptime)
87 {
88 deadline = CLOCKS_NEVER;
89 }
90 else
91 {
92 deadline = uptime + timeout;
93 }
94
95 bool firstCheck = true;
96 while (true)
97 {
99 clock_t remaining = (deadline == CLOCKS_NEVER) ? CLOCKS_NEVER : deadline - uptime;
100 wait_queue_t* queue = &futex->queue;
101 if (wait_block_setup(&queue, 1, remaining) == ERR)
102 {
103 return ERR;
104 }
105
106 uint64_t loadedVal;
107 if (thread_load_atomic_from_user(thread, addr, &loadedVal) == ERR)
108 {
110 return ERR;
111 }
112
113 if (loadedVal != val)
114 {
115 if (firstCheck)
116 {
117 errno = EAGAIN;
118 }
120 return 0;
121 }
122 firstCheck = false;
123
124 // If a FUTEX_WAKE was called in between the check and the wait_block_commit() then we will unblock
125 // immediately.
126 if (wait_block_commit() == ERR)
127 {
128 LOG_DEBUG("futex wait interrupted tid=%d\n", thread->id);
129 return ERR;
130 }
131 }
132 }
133 break;
134 case FUTEX_WAKE:
135 {
136 if (wait_unblock(&futex->queue, val, EOK) == ERR)
137 {
138 return ERR;
139 }
140 }
141 break;
142 default:
143 {
144 errno = EINVAL;
145 return ERR;
146 }
147 }
148 return 0;
149}
#define CLOCKS_NEVER
Definition clock_t.h:16
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscalls.h:100
#define SYS_FUTEX
Definition syscalls.h:49
#define LOG_DEBUG(format,...)
Definition log.h:81
uint64_t thread_load_atomic_from_user(thread_t *thread, atomic_uint64_t *userObj, uint64_t *outValue)
Atomically load a 64-bit value from a user-space atomic variable.
Definition thread.c:336
uint64_t wait_block_commit(void)
Block the currently running thread.
Definition wait.c:321
uint64_t wait_block_setup(wait_queue_t **waitQueues, uint64_t amount, clock_t timeout)
Setup blocking but dont block yet.
Definition wait.c:227
void wait_block_cancel(void)
Cancel blocking.
Definition wait.c:300
uint64_t wait_unblock(wait_queue_t *waitQueue, uint64_t amount, errno_t err)
Unblock threads waiting on a wait queue.
Definition wait.c:168
void wait_queue_init(wait_queue_t *waitQueue)
Initialize wait queue.
Definition wait.c:71
void wait_queue_deinit(wait_queue_t *waitQueue)
Deinitialize wait queue.
Definition wait.c:77
thread_t * sched_thread(void)
Retrieves the currently running thread.
Definition sched.c:157
void futex_ctx_deinit(futex_ctx_t *ctx)
Deinitialize a per-process futex context. *.
Definition futex.c:21
void futex_ctx_init(futex_ctx_t *ctx)
Initialize a per-process futex context.
Definition futex.c:15
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:80
#define LOCK_SCOPE(lock)
Acquires a lock for the reminder of the current scope.
Definition lock.h:57
clock_t timer_uptime(void)
Time since boot.
Definition timer.c:73
void map_deinit(map_t *map)
Deinitialize a map.
Definition map.c:182
void map_entry_init(map_entry_t *entry)
Initialize a map entry.
Definition map.c:71
static map_key_t map_key_uint64(uint64_t uint64)
Create a map key from a uint64_t.
Definition map.h:115
uint64_t map_insert(map_t *map, const map_key_t *key, map_entry_t *value)
Insert a key-value pair into the map.
Definition map.c:191
uint64_t map_init(map_t *map)
Initialize a map.
Definition map.c:172
#define MAP_ENTRY_PTR_IS_VALID(entryPtr)
Check if a map entry pointer is valid (not NULL or tombstone).
Definition map.h:67
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
Definition map.c:236
#define EINVAL
Invalid argument.
Definition errno.h:142
#define errno
Error number variable.
Definition errno.h:27
#define EOK
No error.
Definition errno.h:32
#define EAGAIN
Try again.
Definition errno.h:87
futex_op_t
Futex operation enum.
Definition proc.h:226
uint64_t futex(atomic_uint64_t *addr, uint64_t val, futex_op_t op, clock_t timeout)
System call for fast user space mutual exclusion.
Definition futex.c:6
clock_t uptime(void)
System call for retreving the time since boot.
Definition uptime.c:6
@ FUTEX_WAKE
The futex operation for waking up a amount of threads specified by the val argument.
Definition proc.h:228
@ FUTEX_WAIT
The futex operating for waiting until the value pointed to by addr is not equal to val.
Definition proc.h:227
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF(ptr, type, member)
Container of macro.
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
static futex_t * futex_ctx_get(futex_ctx_t *ctx, void *addr)
Definition futex.c:38
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
Per-process futex context.
Definition futex.h:34
lock_t lock
Definition futex.h:36
map_t futexes
Definition futex.h:35
Futex structure.
Definition futex.h:24
Map entry structure.
Definition map.h:57
Map key stucture.
Definition map.h:45
uint64_t capacity
Definition map.h:78
map_entry_t ** entries
Definition map.h:77
Process structure.
Definition process.h:53
futex_ctx_t futexCtx
Definition process.h:62
space_t space
Definition process.h:59
Virtual address space structure.
Definition space.h:79
Thread of execution structure.
Definition thread.h:55
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
tid_t id
The thread id, unique within a process_t.
Definition thread.h:59
Wait queue structure.
Definition wait.h:166