/*
 * 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>
#include <syscall_handler.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);

	SYS_PORT_TRACING_OBJ_INIT(k_condvar, condvar, 0);

	return 0;
}

#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_init(struct k_condvar *condvar)
{
	Z_OOPS(Z_SYSCALL_OBJ_INIT(condvar, K_OBJ_CONDVAR));
	return z_impl_k_condvar_init(condvar);
}
#include <syscalls/k_condvar_init_mrsh.c>
#endif

int z_impl_k_condvar_signal(struct k_condvar *condvar)
{
	k_spinlock_key_t key = k_spin_lock(&lock);

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, signal, condvar);

	struct k_thread *thread = z_unpend_first_thread(&condvar->wait_q);

	if (thread != NULL) {
		SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_condvar, signal, condvar, K_FOREVER);

		arch_thread_return_value_set(thread, 0);
		z_ready_thread(thread);
		z_reschedule(&lock, key);
	} else {
		k_spin_unlock(&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)
{
	Z_OOPS(Z_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
	return z_impl_k_condvar_signal(condvar);
}
#include <syscalls/k_condvar_signal_mrsh.c>
#endif

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);

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, broadcast, condvar);

	/* wake up any threads that are waiting to write */
	while ((pending_thread = z_unpend_first_thread(&condvar->wait_q)) !=
	       NULL) {
		woken++;
		arch_thread_return_value_set(pending_thread, 0);
		z_ready_thread(pending_thread);
	}

	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, broadcast, condvar, woken);

	z_reschedule(&lock, key);

	return woken;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_broadcast(struct k_condvar *condvar)
{
	Z_OOPS(Z_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
	return z_impl_k_condvar_broadcast(condvar);
}
#include <syscalls/k_condvar_broadcast_mrsh.c>
#endif

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;

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_condvar, wait, condvar);

	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);

	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_condvar, wait, condvar, ret);

	return ret;
}
#ifdef CONFIG_USERSPACE
int z_vrfy_k_condvar_wait(struct k_condvar *condvar, struct k_mutex *mutex,
			  k_timeout_t timeout)
{
	Z_OOPS(Z_SYSCALL_OBJ(condvar, K_OBJ_CONDVAR));
	Z_OOPS(Z_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
	return z_impl_k_condvar_wait(condvar, mutex, timeout);
}
#include <syscalls/k_condvar_wait_mrsh.c>
#endif