blob: 1ee79089091baaeab55779e5f960a9586942d9df [file]
/*
* Copyright (c) 2020 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/toolchain.h>
#include <ksched.h>
#include <scheduler.h>
#include <wait_q.h>
#include <zephyr/internal/syscall_handler.h>
#include <zephyr/init.h>
#ifdef CONFIG_OBJ_CORE_CONDVAR
static struct k_obj_type obj_type_condvar;
#endif /* CONFIG_OBJ_CORE_CONDVAR */
static struct k_spinlock condvar_lock;
int z_impl_k_condvar_init(struct k_condvar *condvar)
{
z_waitq_init(&condvar->wait_q);
k_object_init(condvar);
#ifdef CONFIG_OBJ_CORE_CONDVAR
k_obj_core_init_and_link(K_OBJ_CORE(condvar), &obj_type_condvar);
#endif /* CONFIG_OBJ_CORE_CONDVAR */
SYS_PORT_TRACING_OBJ_INIT(k_condvar, condvar, 0);
return 0;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_init(struct k_condvar *condvar)
{
K_OOPS(K_SYSCALL_OBJ_INIT(condvar, K_OBJ_CONDVAR));
return z_impl_k_condvar_init(condvar);
}
#include <zephyr/syscalls/k_condvar_init_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_k_condvar_signal(struct k_condvar *condvar)
{
k_spinlock_key_t key = k_spin_lock(&condvar_lock);
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, signal, condvar);
if (z_sched_wake(&condvar->wait_q, 0, NULL)) {
SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_condvar, signal, condvar, K_FOREVER);
z_reschedule(&condvar_lock, key);
} else {
k_spin_unlock(&condvar_lock, key);
}
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, signal, condvar, 0);
return 0;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_signal(struct k_condvar *condvar)
{
K_OOPS(K_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
return z_impl_k_condvar_signal(condvar);
}
#include <zephyr/syscalls/k_condvar_signal_mrsh.c>
#endif /* CONFIG_USERSPACE */
int z_impl_k_condvar_broadcast(struct k_condvar *condvar)
{
k_spinlock_key_t key;
int woken = 0;
key = k_spin_lock(&condvar_lock);
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, broadcast, condvar);
/* wake up any threads that are waiting to write */
while (z_sched_wake(&condvar->wait_q, 0, NULL)) {
woken++;
}
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, broadcast, condvar, woken);
if (woken == 0) {
k_spin_unlock(&condvar_lock, key);
} else {
z_reschedule(&condvar_lock, key);
}
return woken;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_broadcast(struct k_condvar *condvar)
{
K_OOPS(K_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
return z_impl_k_condvar_broadcast(condvar);
}
#include <zephyr/syscalls/k_condvar_broadcast_mrsh.c>
#endif /* CONFIG_USERSPACE */
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 = -EAGAIN;
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, wait, condvar, timeout);
if (unlikely(K_TIMEOUT_EQ(timeout, K_NO_WAIT))) {
/* No wait: per POSIX semantics, the mutex must remain locked when
* k_condvar_wait() returns. Returning -EAGAIN immediately without
* touching the mutex preserves the calling-thread-holds-mutex
* invariant.
*/
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, wait, condvar, timeout, ret);
return ret;
}
key = k_spin_lock(&condvar_lock);
k_mutex_unlock(mutex);
ret = z_pend_curr(&condvar_lock, key, &condvar->wait_q, timeout);
/* Always re-acquire the mutex before returning, even on timeout.
* This matches POSIX semantics: the mutex must be locked by the
* calling thread when pthread_cond_wait() returns, regardless of
* whether it was signaled or timed out.
*/
k_mutex_lock(mutex, K_FOREVER);
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, wait, condvar, timeout, ret);
return ret;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex,
k_timeout_t timeout)
{
K_OOPS(K_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
K_OOPS(K_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
return z_impl_k_condvar_wait(condvar, mutex, timeout);
}
#include <zephyr/syscalls/k_condvar_wait_mrsh.c>
#endif /* CONFIG_USERSPACE */
#ifdef CONFIG_OBJ_CORE_CONDVAR
K_OBJ_TYPE_DEFINE(obj_type_condvar, k_condvar, K_OBJ_TYPE_CONDVAR_ID, NULL);
#endif /* CONFIG_OBJ_CORE_CONDVAR */