//! \~\file piconditionvar.h
//! \~\ingroup Thread
//! \~\brief
//! \~english Condition variable for waiting and notification between threads
//! \~russian Переменная условия для ожидания и уведомления между потоками
/*
	PIP - Platform Independent Primitives
	Condition variable for waiting and notification between threads
	Stephan Fomenko

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PICONDITIONVAR_H
#define PICONDITIONVAR_H

#include "pimutex.h"
#include "pisystemtime.h"

//! \~\ingroup Thread
//! \~\brief
//! \~english Condition variable used together with external %PIMutex.
//! \~russian Переменная условия, используемая вместе с внешним %PIMutex.
class PIP_EXPORT PIConditionVariable {
public:
	NO_COPY_CLASS(PIConditionVariable);

	//! \~english Constructs condition variable.
	//! \~russian Создает переменную условия.
	explicit PIConditionVariable();

	//! \~english Destroys condition variable.
	//! \~russian Уничтожает переменную условия.
	virtual ~PIConditionVariable();

	//! \~english Wakes one waiting thread, if any.
	//! \~russian Пробуждает один ожидающий поток, если он есть.
	void notifyOne();

	//! \~english Wakes all threads currently waiting on this variable.
	//! \~russian Пробуждает все потоки, ожидающие на этой переменной.
	void notifyAll();

	//! \~english Waits until notification, temporarily releasing \a lk while blocked.
	//! \~russian Ожидает уведомления, временно освобождая \a lk на время блокировки.
	virtual void wait(PIMutex & lk);

	//! \~\brief
	//! \~english Waits until \a condition becomes true, rechecking it after each wakeup while \a lk is locked.
	//! \~russian Ожидает, пока \a condition не станет истинным, повторно проверяя его после каждого пробуждения при заблокированном \a lk.
	//! \~\details
	//! \~english
	//! The execution of the current thread (which shall have locked with lk method PIMutex::lock()) is blocked
	//! until notified.
	//!
	//! At the moment of blocking the thread, the function automatically calls lk.unlock() (PIMutex::unlock()),
	//! allowing other locked threads to continue.
	//!
	//! Once notified (explicitly, by some other thread), the function unblocks and calls lk.lock() (PIMutex::lock()),
	//! leaving lk in the same state as when the function was called. Then the function returns (notice that this last mutex
	//! locking may block again the thread before returning).
	//!
	//! Generally, the function is notified to wake up by a call in another thread either to member notifyOne() or to
	//! member notifyAll(). But certain implementations may produce spurious wake-up calls without any of these functions
	//! being called. Therefore, users of this function shall ensure their condition for resumption is met.
	//!
	//! If condition is specified, the function only blocks if condition returns false, and notifications can only unblock
	//! the thread when it becomes true (which is specially useful to check against spurious wake-up calls).
	//!
	//! \param lk lock object used by method wait for data protection
	//! \param condition A callable object or function that takes no arguments and returns a value that can be evaluated
	//! as a bool. This is called repeatedly until it evaluates to true.
	//!
	//! \~russian
	//! Выполнение текущего потока (который должен быть заблокирован с помощью \a lk методом PIMutex::lock())
	//! приостанавливается до получения уведомления.
	//!
	//! В момент блокировки потока функция автоматически вызывает lk.unlock() (PIMutex::unlock()),
	//! позволяя другим заблокированным потокам продолжить выполнение.
	//!
	//! После получения уведомления (явного, от другого потока) функция разблокируется и вызывает
	//! lk.lock() (PIMutex::lock()), возвращая lk в то же состояние, в котором он находился при вызове
	//! функции. Затем функция завершается (обратите внимание, что эта последняя блокировка мьютекса
	//! может снова заблокировать поток перед возвратом).
	//!
	//! Обычно функция пробуждается при вызове в другом потоке либо notifyOne(), либо notifyAll().
	//! Однако некоторые реализации могут создавать ложные пробуждения без вызова любой из этих функций.
	//! Поэтому пользователи этой функции должны убедиться, что условие для возобновления выполнено.
	//!
	//! Если указано condition, функция блокируется только если condition возвращает false,
	//! а уведомления могут разблокировать поток только когда оно станет true (что особенно полезно
	//! для проверки ложных пробуждений).
	//!
	//! \param lk объект блокировки, используемый методом wait для защиты данных
	//! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как
	//! bool. Вызывается повторно, пока не примет значение true
	//!
	virtual void wait(PIMutex & lk, std::function<bool ()> condition);


	//! \~english Waits for at most \a timeout and returns \c true if awakened before it expires.
	//! \~russian Ожидает не дольше \a timeout и возвращает \c true, если пробуждение произошло до его истечения.
	virtual bool waitFor(PIMutex & lk, PISystemTime timeout);

	//! \brief
	//! \~english Waits until \a condition becomes true or \a timeout expires.
	//! \~russian Ожидает, пока \a condition не станет истинным или не истечет \a timeout.
	//! \~\details
	//! \~english
	//! The execution of the current thread (which shall have locked with lk method PIMutex::lock()) is blocked
	//! during timeout, or until notified (if the latter happens first).
	//!
	//! At the moment of blocking the thread, the function automatically calls lk.lock() (PIMutex::lock()), allowing
	//! other locked threads to continue.
	//!
	//! Once notified or once timeout has passed, the function unblocks and calls lk.unlock() (PIMutex::unlock()),
	//! leaving lk in the same state as when the function was called. Then the function returns (notice that this last
	//! mutex locking may block again the thread before returning).
	//!
	//! Generally, the function is notified to wake up by a call in another thread either to member notifyOne() or to
	//! member notifyAll(). But certain implementations may produce spurious wake-up calls without any of these functions
	//! being called. Therefore, users of this function shall ensure their condition for resumption is met.
	//!
	//! If condition is specified, the function only blocks if condition returns false, and notifications can only unblock
	//! the thread when it becomes true (which is especially useful to check against spurious wake-up calls).
	//!
	//! \param lk lock object used by method wait for data protection
	//! \param condition A callable object or function that takes no arguments and returns a value that can be evaluated
	//! as a bool. This is called repeatedly until it evaluates to true.
	//! \return false if timeout reached or true if wakeup condition is true
	//!
	//! \~russian
	//! Выполнение текущего потока (который должен быть заблокирован с помощью lk методом PIMutex::lock())
	//! приостанавливается на время timeout, или до получения уведомления (в зависимости от того,
	//! что произойдет раньше).
	//!
	//! В момент блокировки потока функция автоматически вызывает lk.unlock() (PIMutex::unlock()),
	//! позволяя другим заблокированным потокам продолжить выполнение.
	//!
	//! После получения уведомления или истечения таймаута функция разблокируется и вызывает
	//! lk.lock() (PIMutex::lock()), возвращая lk в то же состояние, в котором он находился при вызове
	//! функции. Затем функция завершается (обратите внимание, что эта последняя блокировка мьютекса
	//! может снова заблокировать поток перед возвратом).
	//!
	//! Обычно функция пробуждается при вызове в другом потоке либо notifyOne(), либо notifyAll().
	//! Однако некоторые реализации могут создавать ложные пробуждения без вызова любой из этих функций.
	//! Поэтому пользователи этой функции должны убедиться, что условие для возобновления выполнено.
	//!
	//! Если указано condition, функция блокируется только если condition возвращает false,
	//! а уведомления могут разблокировать поток только когда оно станет true (что особенно полезно
	//! для проверки ложных пробуждений).
	//!
	//! \param lk объект блокировки, используемый методом wait для защиты данных
	//! \param timeout время ожидания
	//! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как
	//! bool. Вызывается повторно, пока не примет значение true
	//! \return false если достигнут таймаут, или true если условие пробуждения истинно
	//!
	virtual bool waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition);

private:
	PRIVATE_DECLARATION(PIP_EXPORT)
};


#endif // PICONDITIONVAR_H
