kernel: add condition variables

Introduce condition variables similar to how they are done in POSIX with
a mutex.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Anas Nashif 2020-08-23 12:39:09 -04:00
commit 06eb489c45
4 changed files with 151 additions and 0 deletions

View file

@ -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 <name>; @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
*/

View file

@ -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))
{

View file

@ -24,6 +24,7 @@ list(APPEND kernel_files
thread_abort.c
version.c
work_q.c
condvar.c
smp.c
banner.c
)

71
kernel/condvar.c Normal file
View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2020 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <kernel_structs.h>
#include <toolchain.h>
#include <ksched.h>
#include <wait_q.h>
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;
}