/*! \file pireadwritelock.h
 * \ingroup Thread
 * \~\brief
 * \~english Read-write lock with multiple readers or one writer
 * \~russian Блокировка чтения-записи с несколькими читателями или одним писателем
 */
/*
    PIP - Platform Independent Primitives
    PIReadWriteLock, PIReadLocker, PIWriteLocker
    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 PIREADWRITELOCK_H
#define PIREADWRITELOCK_H

#include "piconditionvar.h"

//! \~\ingroup Thread
//! \~\brief
//! \~english Synchronization primitive that allows concurrent readers and exclusive writer access.
//! \~russian Примитив синхронизации, допускающий одновременных читателей и эксклюзивный доступ писателя.
class PIP_EXPORT PIReadWriteLock {
public:
	NO_COPY_CLASS(PIReadWriteLock)

	//! \~english Constructs %PIReadWriteLock.
	//! \~russian Создает %PIReadWriteLock.
	PIReadWriteLock();

	//! \~english Destroy %PIReadWriteLock.
	//! \~russian Деструктор %PIReadWriteLock.
	~PIReadWriteLock();


	//! \~english Acquires writer access, waiting until there are no active readers or writer.
	//! \~russian Захватывает доступ на запись, ожидая отсутствия активных читателей и писателя.
	void lockWrite();

	//! \~english Tries to acquire writer access without waiting.
	//! \~russian Пытается захватить доступ на запись без ожидания.
	bool tryLockWrite();

	//! \~english Tries to acquire writer access within \a timeout.
	//! \~russian Пытается захватить доступ на запись в пределах \a timeout.
	bool tryLockWrite(PISystemTime timeout);

	//! \~english Releases writer access and wakes waiting threads.
	//! \~russian Освобождает доступ на запись и пробуждает ожидающие потоки.
	void unlockWrite();


	//! \~english Acquires one reader slot, waiting while writer access is active.
	//! \~russian Захватывает одно место читателя, ожидая пока активен доступ на запись.
	void lockRead();

	//! \~english Tries to acquire reader access without waiting.
	//! \~russian Пытается захватить доступ на чтение без ожидания.
	bool tryLockRead();

	//! \~english Tries to acquire reader access within \a timeout.
	//! \~russian Пытается захватить доступ на чтение в пределах \a timeout.
	bool tryLockRead(PISystemTime timeout);

	//! \~english Releases one reader slot and wakes waiting threads.
	//! \~russian Освобождает одно место читателя и пробуждает ожидающие потоки.
	void unlockRead();

private:
	int reading  = 0;
	bool writing = false;
	PIMutex mutex;
	PIConditionVariable var;
};

//! \~\ingroup Thread
//! \~\brief
//! \~english Scope guard that acquires reader access in constructor and releases it in destructor.
//! \~russian Защитник области видимости, который захватывает доступ на чтение в конструкторе и освобождает его в деструкторе.
class PIP_EXPORT PIReadLocker {
public:
	NO_COPY_CLASS(PIReadLocker);

	//! \~english Constructs and lock for read %PIReadWriteLock "l" if "condition" is \c true.
	//! \~russian Создается и блокирует на чтение %PIReadWriteLock "l" если "condition" \c true.
	PIReadLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) {
		if (cond) rwl.lockRead();
	}

	//! \~english Release read lock on %PIReadWriteLock if "condition" was \c true.
	//! \~russian Освобождает блокировку на чтение %PIReadWriteLock если "condition" был \c true.
	~PIReadLocker() {
		if (cond) rwl.unlockRead();
	}

private:
	PIReadWriteLock & rwl;
	bool cond = true;
};

//! \~\ingroup Thread
//! \~\brief
//! \~english Scope guard that acquires writer access in constructor and releases it in destructor.
//! \~russian Защитник области видимости, который захватывает доступ на запись в конструкторе и освобождает его в деструкторе.
class PIP_EXPORT PIWriteLocker {
public:
	NO_COPY_CLASS(PIWriteLocker);

	//! \~english Constructs and lock for write %PIReadWriteLock "l" if "condition" is \c true.
	//! \~russian Создается и блокирует на запись %PIReadWriteLock "l" если "condition" \c true.
	PIWriteLocker(PIReadWriteLock & l, bool condition = true): rwl(l), cond(condition) {
		if (cond) rwl.lockWrite();
	}

	//! \~english Release write lock on %PIReadWriteLock if "condition" was \c true.
	//! \~russian Освобождает блокировку на запись %PIReadWriteLock если "condition" был \c true.
	~PIWriteLocker() {
		if (cond) rwl.unlockWrite();
	}

private:
	PIReadWriteLock & rwl;
	bool cond = true;
};


#endif // PIREADWRITELOCK_H
