/*
	PIP - Platform Independent Primitives
	
	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 "pithread.h"


/**
 * @brief A condition variable is an object able to block the calling thread until notified to resume.
 *
 * It uses a PIMutex to lock the thread when one of its wait functions is called. The thread remains
 * blocked until woken up by another thread that calls a notification function on the same PIConditionVariable object.
 */
class PIP_EXPORT PIConditionVariable {
public:
	NO_COPY_CLASS(PIConditionVariable)
	explicit PIConditionVariable();
	virtual ~PIConditionVariable();

	/**
	 * @brief Unblocks one of the threads currently waiting for this condition. If no threads are waiting, the function
	 * does nothing. If more than one, it is unspecified which of the threads is selected.
	 */
	void notifyOne();

	/**
	 * @brief Unblocks all threads currently waiting for this condition. If no threads are waiting, the function does
	 * nothing.
	 */
	void notifyAll();

	/**
	 * @brief see wait(PIMutex &, const std::function<bool()>&)
	 */
	virtual void wait(PIMutex & lk);

	/**
	 * @brief Wait until notified
	 *
	 * 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.
	 */
	virtual void wait(PIMutex& lk, const std::function<bool()>& condition);

	/**
	 * @brief see waitFor(PIMutex &, int, const std::function<bool()>&)
	 */
	virtual bool waitFor(PIMutex & lk, int timeoutMs);

	/**
	 * @brief Wait for timeout or until notified
	 *
	 * The execution of the current thread (which shall have locked with lk method PIMutex::lock()) is blocked
	 * during timeoutMs, 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 timeoutMs 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
	 */
	virtual bool waitFor(PIMutex& lk, int timeoutMs, const std::function<bool()>& condition);

private:
	PRIVATE_DECLARATION(PIP_EXPORT)
};


#endif // PICONDITIONVAR_H
