/*! \file pisemaphore.h
 * \ingroup Thread
 * \~\brief
 * \~english Counting semaphore for shared resources
 * \~russian Счетный семафор для общих ресурсов
 */
/*
    PIP - Platform Independent Primitives
    PISemaphore, PISemaphoreLocker
    Ivan Pelipenko peri4ko@yandex.ru

    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 PISEMAPHORE_H
#define PISEMAPHORE_H

#include "piconditionvar.h"

//! \~\ingroup Thread
//! \~\brief
//! \~english Counting semaphore that tracks available resource units.
//! \~russian Счетный семафор, отслеживающий количество доступных единиц ресурса.
class PIP_EXPORT PISemaphore {
public:
	NO_COPY_CLASS(PISemaphore)

	//! \~english Constructs semaphore with \"initial\" available resources.
	//! \~russian Создает семафор с \"initial\" начальными свободными ресурсами.
	PISemaphore(int initial = 0);

	//! \~english Destroy semaphore.
	//! \~russian Деструктор семафора.
	~PISemaphore();


	//! \~english Acquires \a cnt resource units, waiting until enough units become available.
	//! \~russian Захватывает \a cnt единиц ресурса, ожидая появления достаточного количества.
	void acquire(int cnt = 1);

	//! \~english Tries to acquire \a cnt resource units without waiting.
	//! \~russian Пытается захватить \a cnt единиц ресурса без ожидания.
	bool tryAcquire(int cnt = 1);

	//! \~english Tries to acquire \a cnt resource units within \a timeout.
	//! \~russian Пытается захватить \a cnt единиц ресурса в пределах \a timeout.
	bool tryAcquire(int cnt, PISystemTime timeout);

	//! \~english Releases \a cnt resource units and wakes waiting threads.
	//! \~russian Освобождает \a cnt единиц ресурса и пробуждает ожидающие потоки.
	void release(int cnt = 1);

	//! \~english Returns the current number of available resource units.
	//! \~russian Возвращает текущее количество доступных единиц ресурса.
	int available() const;

private:
	int count = 0;
	mutable PIMutex mutex;
	PIConditionVariable var;
};

//! \~\ingroup Thread
//! \~\brief
//! \~english Scope guard that acquires semaphore units in constructor and releases them in destructor.
//! \~russian Защитник области видимости, который захватывает единицы семафора в конструкторе и освобождает их в деструкторе.
class PIP_EXPORT PISemaphoreLocker {
public:
	NO_COPY_CLASS(PISemaphoreLocker);

	//! \~english Constructs and acquire \"cnt\" resources on semaphore "s" if "condition" is \c true.
	//! \~russian Создается и захватывает \"cnt\" ресурсов у семафора "s" если "condition" \c true.
	PISemaphoreLocker(PISemaphore & s, int cnt = 1, bool condition = true): sem(s), count(cnt), cond(condition) {
		if (cond) sem.acquire(count);
	}

	//! \~english Release "cnt" resources on semaphore if "condition" was \c true.
	//! \~russian Освобождает "cnt" ресурсов у семафора если "condition" был \c true.
	~PISemaphoreLocker() {
		if (cond) sem.release(count);
	}

private:
	PISemaphore & sem;
	int count = 1;
	bool cond = true;
};


#endif // PISEMAPHORE_H
