/*
 * Copyright (c) 2019 Intel corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/kernel_structs.h>
#include <zephyr/spinlock.h>
#include <kswap.h>
#include <zephyr/internal/syscall_handler.h>
#include <zephyr/init.h>
#include <ksched.h>

static struct z_futex_data *k_futex_find_data(struct k_futex *futex)
{
	struct k_object *obj;

	obj = k_object_find(futex);
	if ((obj == NULL) || (obj->type != K_OBJ_FUTEX)) {
		return NULL;
	}

	return obj->data.futex_data;
}

int z_impl_k_futex_wake(struct k_futex *futex, bool wake_all)
{
	k_spinlock_key_t key;
	unsigned int woken = 0U;
	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 != NULL) {
			woken++;
			arch_thread_return_value_set(thread, 0);
			z_ready_thread(thread);
		}
	} while (thread && wake_all);

	if (woken == 0) {
		k_spin_unlock(&futex_data->lock, key);
	} else {
		z_reschedule(&futex_data->lock, key);
	}

	return woken;
}

static inline int z_vrfy_k_futex_wake(struct k_futex *futex, bool wake_all)
{
	if (K_SYSCALL_MEMORY_WRITE(futex, sizeof(struct k_futex)) != 0) {
		return -EACCES;
	}

	return z_impl_k_futex_wake(futex, wake_all);
}
#include <zephyr/syscalls/k_futex_wake_mrsh.c>

int z_impl_k_futex_wait(struct k_futex *futex, int expected,
			k_timeout_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;
	}

	if (atomic_get(&futex->val) != (atomic_val_t)expected) {
		return -EAGAIN;
	}

	key = k_spin_lock(&futex_data->lock);

	ret = z_pend_curr(&futex_data->lock,
			key, &futex_data->wait_q, timeout);
	if (ret == -EAGAIN) {
		ret = -ETIMEDOUT;
	}

	return ret;
}

static inline int z_vrfy_k_futex_wait(struct k_futex *futex, int expected,
				      k_timeout_t timeout)
{
	if (K_SYSCALL_MEMORY_WRITE(futex, sizeof(struct k_futex)) != 0) {
		return -EACCES;
	}

	return z_impl_k_futex_wait(futex, expected, timeout);
}
#include <zephyr/syscalls/k_futex_wait_mrsh.c>