Browse Source
A k_futex is a lightweight mutual exclusion primitive designed to minimize kernel involvement. Uncontended operation relies only on atomic access to shared memory. k_futex structure lives in application memory. And when using futexes, the majority of the synchronization operations are performed in user mode. A user-mode thread employs the futex wait system call only when it is likely that the program has to block for a longer time until the condition becomes true. When the condition comes true, futex wake operation will be used to wake up one or more threads waiting on that futex. This patch implements two futex operations: k_futex_wait and k_futex_wake. For k_futex_wait, the comparison with the expected value, and starting to sleep are performed atomically to prevent lost wake-ups. If different context changed futex's value after the calling use-mode thread decided to block himself based on the old value, the comparison will help observing the value change and will not start to sleep. And for k_futex_wake, it will wake at most num_waiters of the waiters that are sleeping on that futex. But no guarantees are made on which threads are woken, that means scheduling priority is not taken into consideration. Fixes: #14493. Signed-off-by: Wentong Wu <wentong.wu@intel.com>pull/14794/head
5 changed files with 225 additions and 1 deletions
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Intel corporation |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <kernel.h> |
||||
#include <kernel_structs.h> |
||||
#include <spinlock.h> |
||||
#include <kswap.h> |
||||
#include <syscall_handler.h> |
||||
#include <init.h> |
||||
#include <ksched.h> |
||||
|
||||
static struct z_futex_data *k_futex_find_data(struct k_futex *futex) |
||||
{ |
||||
struct _k_object *obj; |
||||
|
||||
obj = z_object_find(futex); |
||||
if (obj == NULL || obj->type != K_OBJ_FUTEX) { |
||||
return NULL; |
||||
} |
||||
|
||||
return (struct z_futex_data *)obj->data; |
||||
} |
||||
|
||||
void z_impl_k_futex_init(struct k_futex *futex) |
||||
{ |
||||
futex->val = 0U; |
||||
z_object_init(futex); |
||||
} |
||||
|
||||
Z_SYSCALL_HANDLER(k_futex_init, futex) |
||||
{ |
||||
if (Z_SYSCALL_MEMORY_WRITE(futex, sizeof(struct k_futex)) != 0) { |
||||
return -EACCES; |
||||
} |
||||
|
||||
z_impl_k_futex_init((struct k_futex *)futex); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int z_impl_k_futex_wake(struct k_futex *futex, bool wake_all) |
||||
{ |
||||
k_spinlock_key_t key; |
||||
unsigned int woken = 0; |
||||
struct k_thread *thread; |
||||
struct z_futex_data *futex_data; |
||||
|
||||
futex_data = k_futex_find_data(futex); |
||||
if (futex_data == NULL) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
key = k_spin_lock(&futex_data->lock); |
||||
|
||||
do { |
||||
thread = z_unpend_first_thread(&futex_data->wait_q); |
||||
if (thread) { |
||||
z_ready_thread(thread); |
||||
z_set_thread_return_value(thread, 0); |
||||
woken++; |
||||
} |
||||
} while (thread && wake_all); |
||||
|
||||
z_reschedule(&futex_data->lock, key); |
||||
|
||||
return woken; |
||||
} |
||||
|
||||
Z_SYSCALL_HANDLER(k_futex_wake, futex, wake_all) |
||||
{ |
||||
if (Z_SYSCALL_MEMORY_WRITE(futex, sizeof(struct k_futex)) != 0) { |
||||
return -EACCES; |
||||
} |
||||
|
||||
return z_impl_k_futex_wake((struct k_futex *)futex, (bool)wake_all); |
||||
} |
||||
|
||||
int z_impl_k_futex_wait(struct k_futex *futex, int expected, s32_t timeout) |
||||
{ |
||||
int ret; |
||||
k_spinlock_key_t key; |
||||
struct z_futex_data *futex_data; |
||||
|
||||
futex_data = k_futex_find_data(futex); |
||||
if (futex_data == NULL) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
key = k_spin_lock(&futex_data->lock); |
||||
|
||||
if (atomic_get(&futex->val) != (atomic_val_t)expected) { |
||||
k_spin_unlock(&futex_data->lock, key); |
||||
return -EAGAIN; |
||||
} |
||||
|
||||
ret = z_pend_curr(&futex_data->lock, |
||||
key, &futex_data->wait_q, timeout); |
||||
if (ret == -EAGAIN) { |
||||
ret = -ETIMEDOUT; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
Z_SYSCALL_HANDLER(k_futex_wait, futex, expected, timeout) |
||||
{ |
||||
if (Z_SYSCALL_MEMORY_WRITE(futex, sizeof(struct k_futex)) != 0) { |
||||
return -EACCES; |
||||
} |
||||
|
||||
return z_impl_k_futex_wait((struct k_futex *)futex, |
||||
expected, (s32_t)timeout); |
||||
} |
Loading…
Reference in new issue