diff --git a/libs/main/thread/pireadwritelock.cpp b/libs/main/thread/pireadwritelock.cpp new file mode 100644 index 00000000..99ebb209 --- /dev/null +++ b/libs/main/thread/pireadwritelock.cpp @@ -0,0 +1,234 @@ +/* + 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 . +*/ + +//! \addtogroup Thread +//! \{ +//! \class PIReadWriteLock pireadwritelock.h +//! +//! \~\brief +//! \~english Read/Write lock +//! \~russian Блокировка чтения/записи +//! +//! +//! \~\details +//! \~english \section PIReadWriteLock_sec0 Synopsis +//! \~russian \section PIReadWriteLock_sec0 Краткий обзор +//! +//! \~english +//! %PIReadWriteLock provides more complex critical code section defence between several threads. +//! %PIReadWriteLock permit only one thread to modify data, besides multiple thread can read data +//! simultaneously. Conatains methods: +//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() and \a unlockRead(). +//! +//! For automatic read and write locks use \a PIReadLocker and \a PIWriteLocker. +//! +//! \~russian +//! %PIReadWriteLock предоставляет более сложную межпотоковую защиту критических секций кода. +//! %PIReadWriteLock разрешает только одному потоку изменять данные, в то время как читать данные +//! могут одновременно несколько потоков. +//! Содержит методы: +//! \a lockWrite(), \a tryLockWrite(), \a unlockWrite(), \a lockRead(), \a tryLockRead() и \a unlockRead(). +//! +//! Для автоматической блокировки на чтение и запись используйте \a PIReadLocker и \a PIWriteLocker. +//! +//! \~english \section PIReadWriteLock_sec1 Usage +//! \~russian \section PIReadWriteLock_sec1 Использование +//! +//! \~english +//! When one thread lock for write with \a lockWrite(), all other threads (read and write) can`t access data until this thread +//! call \a unlockWrite(). In this way, write lock works similar to mutex. +//! +//! On the other hand, several threads can simultaneously read data. In other words, \a lockRead() increase read threads counter +//! and \a unlockRead() decrease. But thread can`t read data while other thread locked for write. +//! +//! \~russian +//! Когда один поток блокирует запись с помощью функции \a lockWrite(), все остальные потоки (чтение и запись) не смогут получить +//! доступ к данным, пока этот поток не вызовет \a unlockWrite(). Таким образом, блокировка записи работает аналогично мьютексу. +//! +//! С другой стороны, несколько потоков могут одновременно считывать данные. Другими словами, функция \a lockRead() увеличивает счетчик +//! потоков чтения, а функция \a unlockRead() уменьшает. Но поток не может считывать данные, пока другой поток заблокирован для записи. +//! +//! \~\code +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PIReadLocker pireadwritelock.h +//! +//! \~\brief +//! \~english %PIReadWriteLock read autolocker +//! \~russian Автоблокировщик на чтение %PIReadWriteLock +//! +//! +//! \~\details +//! +//! \~english +//! When a %PIReadLocker object is created, it lock %PIReadWriteLock for read, if "condition" \c true. +//! When control leaves the scope in which the %PIReadLocker object was created, +//! the %PIReadLocker is destructed and unlock for read, if "condition" was \c true. +//! +//! If "condition" \c false this class do nothing. +//! +//! The %PIReadLocker class is non-copyable. +//! +//! \~russian +//! При создании экземпляра %PIReadLocker он блокирует %PIReadWriteLock на чтение, если "condition" \c true. +//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на чтение, +//! если "condition" был \c true. +//! +//! Если "condition" \c false, то этот объект ничего не делает. +//! +//! Класс %PIReadLocker некопируемый. +//! +//! \~\code +//! // critical section start +//! { +//! PIReadLocker locker(rw_lock); +//! // ... your read code here +//! } +//! // critical section end +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PIWriteLocker pireadwritelock.h +//! +//! \~\brief +//! \~english %PIReadWriteLock write autolocker +//! \~russian Автоблокировщик на запись %PIReadWriteLock +//! +//! +//! \~\details +//! +//! \~english +//! When a %PIWriteLocker object is created, it lock %PIReadWriteLock for write, if "condition" \c true. +//! When control leaves the scope in which the %PIWriteLocker object was created, +//! the %PIWriteLocker is destructed and unlock for write, if "condition" was \c true. +//! +//! If "condition" \c false this class do nothing. +//! +//! The %PIWriteLocker class is non-copyable. +//! +//! \~russian +//! При создании экземпляра %PIWriteLocker он блокирует %PIReadWriteLock на запись, если "condition" \c true. +//! Когда выполнение покидает область жизни объекта, вызывается его деструктор и освобождается блокировка на запись, +//! если "condition" был \c true. +//! +//! Если "condition" \c false, то этот объект ничего не делает. +//! +//! Класс %PIWriteLocker некопируемый. +//! +//! \~\code +//! // critical section start +//! { +//! PIWriteLocker locker(rw_lock); +//! // ... your write code here +//! } +//! // critical section end +//! \endcode +//! \} + + +#include "pireadwritelock.h" + + +PIReadWriteLock::PIReadWriteLock() {} + + +PIReadWriteLock::~PIReadWriteLock() { + var.notifyAll(); +} + + +void PIReadWriteLock::lockWrite() { + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + var.wait(mutex); + } + writing = true; +} + + +bool PIReadWriteLock::tryLockWrite() { + PIMutexLocker _ml(mutex); + if (reading > 0 || writing) return false; + writing = true; + return true; +} + + +bool PIReadWriteLock::tryLockWrite(PISystemTime timeout) { + PITimeMeasurer tm; + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + PISystemTime remain = timeout - tm.elapsed(); + if (remain.isNegative()) return false; + var.waitFor(mutex, remain); + } + writing = true; + return true; +} + + +void PIReadWriteLock::unlockWrite() { + PIMutexLocker _ml(mutex); + writing = false; + var.notifyAll(); +} + + +void PIReadWriteLock::lockRead() { + PIMutexLocker _ml(mutex); + while (writing) { + var.wait(mutex); + } + ++reading; +} + + +bool PIReadWriteLock::tryLockRead() { + PIMutexLocker _ml(mutex); + if (writing) return false; + ++reading; + return true; +} + + +bool PIReadWriteLock::tryLockRead(PISystemTime timeout) { + PITimeMeasurer tm; + PIMutexLocker _ml(mutex); + while (writing) { + PISystemTime remain = timeout - tm.elapsed(); + if (remain.isNegative()) return false; + var.waitFor(mutex, remain); + } + ++reading; + return true; +} + + +void PIReadWriteLock::unlockRead() { + PIMutexLocker _ml(mutex); + --reading; + var.notifyAll(); +} diff --git a/libs/main/thread/pireadwritelock.h b/libs/main/thread/pireadwritelock.h new file mode 100644 index 00000000..24b08a37 --- /dev/null +++ b/libs/main/thread/pireadwritelock.h @@ -0,0 +1,130 @@ +/*! \file pireadwritelock.h + * \ingroup Read/Write lock + * \~\brief + * \~english Read/Write lock + * \~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 . +*/ + +#ifndef PIREADWRITELOCK_H +#define PIREADWRITELOCK_H + +#include "piconditionvar.h" + + +class PIP_EXPORT PIReadWriteLock { +public: + NO_COPY_CLASS(PIReadWriteLock) + + //! \~english Constructs %PIReadWriteLock. + //! \~russian Создает %PIReadWriteLock. + PIReadWriteLock(); + + //! \~english Destroy %PIReadWriteLock. + //! \~russian Деструктор %PIReadWriteLock. + ~PIReadWriteLock(); + + + //! \~english Lock for write. If already locked for write or read, than wait until all locks released. + //! \~russian Заблокировать на запись. Если уже заблокировано на запись или чтение, то ждёт освобождения блокировок. + void lockWrite(); + + //! \~english Try to lock for write. Returns if operation was successfull. + //! \~russian Пробует заблокировать на запись. Возвращает успех операции. + bool tryLockWrite(); + + //! \~english Try to lock for write for \"timeout\". Returns if operation was successfull (timeout has not expired). + //! \~russian Пробует заблокировать на запись в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут). + bool tryLockWrite(PISystemTime timeout); + + //! \~english Release lock for write. + //! \~russian Освобождает блокировку на запись. + void unlockWrite(); + + + //! \~english Lock for read. If already locked for write, than wait until write lock released. + //! \~russian Заблокировать на чтение. Если уже заблокировано на запись, то ждёт освобождения записывающей блокировки. + void lockRead(); + + //! \~english Try to lock for read. Returns if operation was successfull. + //! \~russian Пробует заблокировать на чтение. Возвращает успех операции. + bool tryLockRead(); + + //! \~english Try to lock for read for \"timeout\". Returns if operation was successfull (timeout has not expired). + //! \~russian Пробует заблокировать на чтение в течении \"timeout\". Возвращает успех операции (не истек ли тайм-аут). + bool tryLockRead(PISystemTime timeout); + + //! \~english Release lock for read. + //! \~russian Освобождает блокировку на чтение. + void unlockRead(); + +private: + int reading = 0; + bool writing = false; + PIMutex mutex; + PIConditionVariable var; +}; + + +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; +}; + + +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 diff --git a/libs/main/thread/pisemaphore.h b/libs/main/thread/pisemaphore.h index 5f964408..7bacbecc 100644 --- a/libs/main/thread/pisemaphore.h +++ b/libs/main/thread/pisemaphore.h @@ -92,4 +92,4 @@ private: }; -#endif // PIMUTEX_H +#endif diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index f5e7c7e3..eba1b509 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -56,6 +56,7 @@ #include "pimutex.h" #include "pipipelinethread.h" #include "piprotectedvariable.h" +#include "pireadwritelock.h" #include "pisemaphore.h" #include "pispinlock.h" #include "pithread.h" diff --git a/main.cpp b/main.cpp index 34c01013..338791b2 100644 --- a/main.cpp +++ b/main.cpp @@ -39,28 +39,66 @@ public: class RWL { public: - void lockWrite() { mutex.lock(); } - void unlockWrite() { mutex.unlock(); } - void lockRead() {} - void unlockRead() {} + void lockWrite() { + PIMutexLocker _ml(mutex); + while (reading > 0 || writing) { + var.wait(mutex); + } + writing = true; + } + void unlockWrite() { + PIMutexLocker _ml(mutex); + writing = false; + var.notifyAll(); + } + void lockRead() { + PIMutexLocker _ml(mutex); + while (writing) { + var.wait(mutex); + } + ++reading; + } + void unlockRead() { + PIMutexLocker _ml(mutex); + --reading; + var.notifyAll(); + } + +private: + PIConditionVariable var; + int reading = 0; + bool writing = false; PIMutex mutex; }; PIMutex mutex; PISemaphore sem(10); +PIReadWriteLock rwl; int main(int argc, char * argv[]) { /*sem.acquire(2); piCout << sem.tryAcquire(2); piCout << sem.available(); return 0;*/ - PIThread t_w( + PIThread t_w0( [] { // PIMutexLocker _ml(mutex); - piCout << "write start ..."; + PIWriteLocker rl(rwl); + piCout << "write0 start ..."; piMSleep(500); - sem.release(11); - piCout << "write end" << sem.available() << "\n"; + piCout << "write0 end" + << "\n"; + }, + true, + 1_Hz); + PIThread t_w1( + [] { + // PIMutexLocker _ml(mutex); + PIWriteLocker rl(rwl); + piCout << "write1 start ..."; + piMSleep(500); + piCout << "write1 end" + << "\n"; }, true, 1_Hz); @@ -69,33 +107,36 @@ int main(int argc, char * argv[]) { PIThread t_r0( [&cnt0] { // PIMutexLocker _ml(mutex); + PIReadLocker rl(rwl); piCout << "read0 start ..."; piMSleep(50); - sem.acquire(2); - bool ok = true; // sem.tryAcquire(2, 100_ms); - if (ok) ++cnt0; - piCout << "read0 end" << sem.available() << ok; + // bool ok = rwl.tryLockRead(100_ms); + // if (ok) ++cnt0; + piCout << "read0 end"; + // if (ok) rwl.unlockRead(); }, true, 10_Hz); PIThread t_r1( [&cnt1] { // PIMutexLocker _ml(mutex); + // PIReadLocker rl(rwl); piCout << "read1 start ..."; piMSleep(50); - sem.acquire(2); - bool ok = true; // sem.tryAcquire(2, 100_ms); + bool ok = rwl.tryLockRead(100_ms); if (ok) ++cnt1; - piCout << "read1 end" << sem.available() << ok; + piCout << "read1 end" << ok; + if (ok) rwl.unlockRead(); }, true, 11_Hz); - piSleep(5.); + piSleep(8.); t_r0.stopAndWait(); t_r1.stopAndWait(); - t_w.stopAndWait(); + t_w0.stopAndWait(); + t_w1.stopAndWait(); piCout << cnt0 << cnt1;