diff --git a/MonitorExample/CMakeLists.txt b/MonitorExample/CMakeLists.txt new file mode 100644 index 0000000..c116e0f --- /dev/null +++ b/MonitorExample/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.14) + +project(MonitorExample LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + +add_executable(MonitorExample + main.cpp + monitor.h monitor.cpp + producer.h producer.cpp + consumer.h consumer.cpp +) +target_link_libraries(MonitorExample Qt${QT_VERSION_MAJOR}::Core) + +include(GNUInstallDirs) +install(TARGETS MonitorExample + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/MonitorExample/consumer.cpp b/MonitorExample/consumer.cpp new file mode 100644 index 0000000..55ca38f --- /dev/null +++ b/MonitorExample/consumer.cpp @@ -0,0 +1,23 @@ +#include "consumer.h" + +template QSemaphore Consumer::numProducts; +template Monitor *Consumer::mon; +template<> void (*Consumer::consume)(int, int) = NULL; + +//--------------------------------------- +template void Consumer::initClass(int numP, Monitor *m, void (*cons)(T, int)) { + numProducts.release(numP); + mon = m; + consume = cons; +} +//--------------------------------------- + +template void Consumer::run() { + while (numProducts.tryAcquire()) { // While not all numProducts are consumed: + T* aux = mon->canGet(); // Get pointer to item in itemQ + T item = *aux; // Take the item out of itemQ + mon->doneGetting(aux); // Give info to producer threads + (*consume)(item,ID); // Consume one item + } +} +//--------------------------------------- diff --git a/MonitorExample/consumer.h b/MonitorExample/consumer.h new file mode 100644 index 0000000..719cc02 --- /dev/null +++ b/MonitorExample/consumer.h @@ -0,0 +1,22 @@ +#ifndef CONSUMER_H +#define CONSUMER_H + +#include "monitor.h" +#include +#include + +template +class Consumer : public QThread { +private: + int ID; + static Monitor *mon; + static QSemaphore numProducts; +public: + static void (*consume)(T i, int); + static void initClass(int numP, Monitor *m, void (*cons)(T, int)); + + Consumer(int i) : ID(i) {} + void run(); +}; + +#endif // CONSUMER_H diff --git a/MonitorExample/main.cpp b/MonitorExample/main.cpp new file mode 100644 index 0000000..722589c --- /dev/null +++ b/MonitorExample/main.cpp @@ -0,0 +1,71 @@ +/* + ============================================================================ + Author : G. Barlas + Version : 1.0, September 2015 + : 1.1, 16.12.2019 - Comments added (H. Weber) + : 1.2, 27.01.2022 - Console output improved (H. Weber, S. Stahl) + License : V 1.0 released under the GNU GPL 3.0 + Description : Producer/consumer example using a monitor. + To compile : qmake monitor2ProdCons.pro; make + ============================================================================ + */ +#include "monitor.h" +#include "producer.h" +#include "consumer.h" + +#include +#include + + +using namespace std; + + +int produce(int id) { + QMutex prodBinSem; + static int i = 0; + + prodBinSem.lock(); + int j = i++; + prodBinSem.unlock(); + + printf("%3d produced by producer %d\n", j, id); // i to j + return j; +} + +void consume(int i, int ID) { + printf("%3d consumed by consumer %d\n", i, ID); +} + +int main(int argc, char *argv[]) { + Monitor m; + + int N = 4; // Count of Producer objects (each is one QThread) + int M = 3; // Count of Consumer objects (each is one QThread) + int numP = 10; // Count of produced/consumed items for all Producer/Consumer threads combined + + printf("%d items produced by %d producers and consumed by %d consumers\n", numP, N, M); + + Producer::initClass(numP, &m, &produce); + Consumer::initClass(numP, &m, &consume); + + Producer *p[N]; + Consumer *c[M]; + //Producers start producing + for (int i = 0; i < N; i++) { + p[i] = new Producer(i); + p[i]->start(); + } + //Consumer start consuming + for (int i = 0; i < M; i++) { + c[i] = new Consumer(i); + c[i]->start(); + } + + + for (int i = 0; i < N; i++) + p[i]->wait(); + for (int i = 0; i < M; i++) + c[i]->wait(); + + return 0; +} diff --git a/MonitorExample/monitor.cpp b/MonitorExample/monitor.cpp new file mode 100644 index 0000000..7d1cd74 --- /dev/null +++ b/MonitorExample/monitor.cpp @@ -0,0 +1,65 @@ +#include "monitor.h" + +template +Monitor::Monitor(int n) { + buffer = new T[n]; + for(int i=0;i +Monitor::~Monitor() { + delete []buffer; +} +//----------------------------------------- + +//producer tries to put an item: +// lock l +// wait for spot in putting queue +// get first spot in putting queue +template +T* Monitor::canPut() { + QMutexLocker ml(&l); // Lock entry controlling mutex l + while (emptySpotsQ.size() == 0) // If no free place in emptySpots queue: + full.wait(&l); // Block until condition variable full is given free by mutex l + T *aux = emptySpotsQ.front(); // Get place in emptySpotsQ + emptySpotsQ.pop(); // ?? Free one place in emptySpotsQ ?? (documentation of class queue currently not available) + return aux; // Return pointer to place in emptySpotsQ for item to be produced +} +//----------------------------------------- + +//consumer tries to take an item: +// lock l +// push produced item to itemQ +// wake waiting consumer +template +T* Monitor::canGet() { + QMutexLocker ml(&l); // Lock entry controlling mutex l + while (itemQ.size() == 0) // If no item in itemQ: + empty.wait(&l); // Block until condition variable empty is given free by mutex l + T* temp = itemQ.front(); // Get item out of itemQ + itemQ.pop(); // ?? Free one place in itemQ ?? (documentation of class queue currently not available) + return temp; // Return pointer to item in itemQ, which will be consumed +} +//----------------------------------------- + +//producer is done with producing and wants to put that item: +// lock l +// push produced item to itemQ +// wake waiting consumer +template +void Monitor::donePutting(T *x) { + QMutexLocker ml(&l); // Lock entry controlling mutex l + itemQ.push(x); // Push place with produced item into itemQ + empty.wakeOne(); // Notify one consumer thread waiting for condition variable empty +} +//----------------------------------------- + +template +void Monitor::doneGetting(T *x) { + QMutexLocker ml(&l); // Lock entry controlling mutex l + emptySpotsQ.push(x); // Give free the buffer place which had hold the consumed item by pushing in into emptySpotsQ + full.wakeOne(); // Notify one producer thread waiting for condition variable empty +} +//************************************************************ diff --git a/MonitorExample/monitor.h b/MonitorExample/monitor.h new file mode 100644 index 0000000..55894d1 --- /dev/null +++ b/MonitorExample/monitor.h @@ -0,0 +1,31 @@ +#ifndef MONITOR_H +#define MONITOR_H +#include +#include +#include + +using namespace std; + +const int BUFFSIZE = 100; +//************************************************************ + +template +class Monitor { +private: + QMutex l; + QWaitCondition full, empty; // full: Condition variable for queue itemQ. + // empty: Condition variable for queue emptySpotsQ. + + queue emptySpotsQ; // Queue of free item buffer places for items to be produced + queue itemQ; // Queue of item buffer places with items to be consumed + T *buffer; // Item buffer +public: + T* canPut(); + T* canGet(); + void donePutting(T *x); + void doneGetting(T *x); + Monitor(int n = BUFFSIZE); + ~Monitor(); +}; + +#endif // MONITOR_H diff --git a/MonitorExample/producer.cpp b/MonitorExample/producer.cpp new file mode 100644 index 0000000..427c4eb --- /dev/null +++ b/MonitorExample/producer.cpp @@ -0,0 +1,25 @@ +#include "producer.h" + +//--------------------------------------- +template QSemaphore Producer::numProducts; +template Monitor * Producer::mon; +template<> int (*Producer::produce)(int) = NULL; +//--------------------------------------- + +template void Producer::initClass(int numP, Monitor *m, T(*prod)(int)) { + mon = m; + numProducts.release(numP); + produce = prod; +} +//--------------------------------------- + +template +void Producer::run() { + while (numProducts.tryAcquire()) { // While not all numProducts items are produced: + T item = (*produce)(ID); // Produce one item + T* aux = mon->canPut(); // Get place for item in emptySpotsQ + *aux = item; // Put item into emptySpotsQ + mon->donePutting(aux); // Give info to consumer threads + } +} +//--------------------------------------- diff --git a/MonitorExample/producer.h b/MonitorExample/producer.h new file mode 100644 index 0000000..f47d4ec --- /dev/null +++ b/MonitorExample/producer.h @@ -0,0 +1,21 @@ +#ifndef PRODUCER_H +#define PRODUCER_H +#include "monitor.h" +#include +#include + +template +class Producer : public QThread { +private: + static QSemaphore numProducts; + int ID; + static Monitor *mon; +public: + static T(*produce)(int); + static void initClass(int numP, Monitor *m, T(*prod)(int)); + + Producer(int i) : ID(i) {} + void run(); +}; + +#endif // PRODUCER_H