From 06eb489c4573a08e8b8d0c59921b958749136c8c Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Sun, 23 Aug 2020 12:39:09 -0400 Subject: [PATCH] kernel: add condition variables Introduce condition variables similar to how they are done in POSIX with a mutex. Signed-off-by: Anas Nashif --- include/kernel.h | 78 ++++++++++++++++++++++++++++++++++++ include/linker/common-ram.ld | 1 + kernel/CMakeLists.txt | 1 + kernel/condvar.c | 71 ++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 kernel/condvar.c diff --git a/include/kernel.h b/include/kernel.h index 36a6c151a1d..b834cb6944f 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -3168,6 +3168,84 @@ __syscall int k_mutex_unlock(struct k_mutex *mutex); * @} */ + +struct k_condvar { + _wait_q_t wait_q; +}; + +#define Z_CONDVAR_INITIALIZER(obj) \ + { \ + .wait_q = Z_WAIT_Q_INIT(&obj.wait_q), \ + } + +/** + * @defgroup condvar_apis Condition Variables APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Initialize a condition variable + * + * @param condvar pointer to a @p k_condvar structure + * @retval 0 Condition variable created successfully + */ +__syscall int k_condvar_init(struct k_condvar *condvar); + +/** + * @brief Signals one thread that is pending on the condition variable + * + * @param condvar pointer to a @p k_condvar structure + * @retval 0 On success + */ +__syscall int k_condvar_signal(struct k_condvar *condvar); + +/** + * @brief Unblock all threads that are pending on the condition + * variable + * + * @param condvar pointer to a @p k_condvar structure + * @return An integer with number of woken threads on success + */ +__syscall int k_condvar_broadcast(struct k_condvar *condvar); + +/** + * @brief Waits on the condition variable releasing the mutex lock + * + * Automically releases the currently owned mutex, blocks the current thread + * waiting on the condition variable specified by @a condvar, + * and finally acquires the mutex again. + * + * The waiting thread unblocks only after another thread calls + * k_condvar_signal, or k_condvar_broadcast with the same condition variable. + * + * @param condvar pointer to a @p k_condvar structure + * @param mutex Address of the mutex. + * @param timeout Waiting period for the condition variable + * or one of the special values K_NO_WAIT and K_FOREVER. + * @retval 0 On success + * @retval -EAGAIN Waiting period timed out. + */ +__syscall int k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex, + k_timeout_t timeout); + +/** + * @brief Statically define and initialize a condition variable. + * + * The condition variable can be accessed outside the module where it is + * defined using: + * + * @code extern struct k_condvar ; @endcode + * + * @param name Name of the condition variable. + */ +#define K_CONDVAR_DEFINE(name) \ + Z_STRUCT_SECTION_ITERABLE(k_condvar, name) = \ + Z_CONDVAR_INITIALIZER(name) +/** + * @} + */ + /** * @cond INTERNAL_HIDDEN */ diff --git a/include/linker/common-ram.ld b/include/linker/common-ram.ld index 005e4f4861d..88cdd4f486f 100644 --- a/include/linker/common-ram.ld +++ b/include/linker/common-ram.ld @@ -102,6 +102,7 @@ Z_ITERABLE_SECTION_RAM_GC_ALLOWED(k_pipe, 4) Z_ITERABLE_SECTION_RAM_GC_ALLOWED(k_sem, 4) Z_ITERABLE_SECTION_RAM_GC_ALLOWED(k_queue, 4) + Z_ITERABLE_SECTION_RAM_GC_ALLOWED(k_condvar, 4) SECTION_DATA_PROLOGUE(_net_buf_pool_area,,SUBALIGN(4)) { diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cde6447d64d..b1278daa643 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND kernel_files thread_abort.c version.c work_q.c + condvar.c smp.c banner.c ) diff --git a/kernel/condvar.c b/kernel/condvar.c new file mode 100644 index 00000000000..c3010cdf10d --- /dev/null +++ b/kernel/condvar.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +static struct k_spinlock lock; + +int z_impl_k_condvar_init(struct k_condvar *condvar) +{ + z_waitq_init(&condvar->wait_q); + z_object_init(condvar); + return 0; +} + +int z_impl_k_condvar_signal(struct k_condvar *condvar) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + struct k_thread *thread = z_unpend_first_thread(&condvar->wait_q); + + if (thread != NULL) { + arch_thread_return_value_set(thread, 0); + z_ready_thread(thread); + z_reschedule(&lock, key); + } else { + k_spin_unlock(&lock, key); + } + return 0; +} + +int z_impl_k_condvar_broadcast(struct k_condvar *condvar) +{ + struct k_thread *pending_thread; + k_spinlock_key_t key; + int woken = 0; + + key = k_spin_lock(&lock); + + /* wake up any threads that are waiting to write */ + while ((pending_thread = z_unpend_first_thread(&condvar->wait_q)) != + NULL) { + arch_thread_return_value_set(pending_thread, 0); + z_ready_thread(pending_thread); + woken++; + } + + z_reschedule(&lock, key); + + return woken; +} + +int z_impl_k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex, + k_timeout_t timeout) +{ + k_spinlock_key_t key; + int ret; + + key = k_spin_lock(&lock); + k_mutex_unlock(mutex); + + ret = z_pend_curr(&lock, key, &condvar->wait_q, timeout); + k_mutex_lock(mutex, K_FOREVER); + + return ret; +}