// SPDX-FileCopyrightText: 2009-2020 thestr4ng3r <info@florianmaerkl.de>
// SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
// SPDX-License-Identifier: LGPL-3.0-only

#include "thread.h"

/**
 * \brief Condition variables are intended to be used to communicate changes in the state of data shared between threads.
 * Condition variables are always associated with a mutex to provide synchronized access to the shared data.
 *
 * \return On success returns a valid pointer to a RzThreadCond structure.
 */
RZ_API RZ_OWN RzThreadCond *rz_th_cond_new(void) {
	RzThreadCond *cond = RZ_NEW0(RzThreadCond);
	if (!cond) {
		return NULL;
	}
#if HAVE_PTHREAD
	if (pthread_cond_init(&cond->cond, NULL) != 0) {
		free(cond);
		return NULL;
	}
#elif __WINDOWS__
	InitializeConditionVariable(&cond->cond);
#endif
	return cond;
}

/**
 * \brief  This function shall unblock at least one of the threads that are blocked on the specified condition
 *
 * \param  cond The RzThreadCond to use for signalling a waiting thread
 */
RZ_API void rz_th_cond_signal(RZ_NONNULL RzThreadCond *cond) {
	rz_return_if_fail(cond);
#if HAVE_PTHREAD
	pthread_cond_signal(&cond->cond);
#elif __WINDOWS__
	WakeConditionVariable(&cond->cond);
#endif
}

/**
 * \brief  This function shall unblock all threads currently blocked on the specified condition
 *
 * \param  cond The RzThreadCond to use for signalling all waiting threads
 */
RZ_API void rz_th_cond_signal_all(RZ_NONNULL RzThreadCond *cond) {
	rz_return_if_fail(cond);
#if HAVE_PTHREAD
	pthread_cond_broadcast(&cond->cond);
#elif __WINDOWS__
	WakeAllConditionVariable(&cond->cond);
#endif
}

/**
 * \brief  The function shall block on a condition variable and shall be called with RzThreadLock locked by the calling thread.
 *
 * \param  cond  The RzThreadCond to use for waiting the signal
 * \param  lock  The RzThreadLock lock to use (the lock must be already taken by the thread)
 */
RZ_API void rz_th_cond_wait(RZ_NONNULL RzThreadCond *cond, RZ_NONNULL RzThreadLock *lock) {
	rz_return_if_fail(cond);
#if HAVE_PTHREAD
	pthread_cond_wait(&cond->cond, &lock->lock);
#elif __WINDOWS__
	SleepConditionVariableCS(&cond->cond, &lock->lock, INFINITE);
#endif
}

/**
 * \brief  Frees a RzThreadCond struct
 *
 * \param  cond  The RzThreadCond to free
 */
RZ_API void rz_th_cond_free(RZ_NULLABLE RzThreadCond *cond) {
	if (!cond) {
		return;
	}
#if HAVE_PTHREAD
	pthread_cond_destroy(&cond->cond);
#endif
	free(cond);
}
