buffer.cpp

URL: https://mirkwood.cs.edinboro.edu/~bennett/class/cmsc4000/spring2026/notes/ch6/code/buffer.cpp
 
#include <iostream>
#include <semaphore>

#include<queue>

#include <wait.h>

#include <thread>
#include <vector>

// I love C++

using namespace std;

const int MAX_SIZE{5};

counting_semaphore<MAX_SIZE> queueEmpty(MAX_SIZE);
counting_semaphore<MAX_SIZE> queueFull(0);

binary_semaphore queueMutex(1);
binary_semaphore outputMutex(1);
queue<string> Q;

void Consumer(int id);
void Producer(ArgT * arg);
void PostMessage(string message, int id);
void Print(string message);

int emptyCount {MAX_SIZE};
int fullCount{0};


int main() {

   int producers{4};
   int consumers{3};
   int messages{6};
   vector<thread> producerThreads;
   vector<thread> consumerThreads;

   // make the producers
   for(int i =0; i < producers; ++i) {
       producerThreads.emplace_back(Producer, i, messages);
   }

   // make the consumers
   for(int i =0; i < consumers; ++i) {
       consumerThreads.emplace_back(Consumer, i+100);
   }

   // collect all of the producers
   for(auto & thread: producerThreads) {
       if(thread.joinable()) {
           thread.join();
       }
   }

   // send a message to all of the consumers to get them to exit
   for(int i = 0; i < consumers; ++i) {
       PostMessage("done", 0);
   }

   // collect all of the consumers
   for(auto & thread: consumerThreads) {
       if(thread.joinable()) {
           thread.join();
       }
   }

   return(0);
}

string GetMessage(int id ) {
    queueFull.acquire();
    queueMutex.acquire();
    
    emptyCount ++;
    fullCount --;
    Print("\t" + to_string(id) + "\tGot a message, empty "
          + to_string(emptyCount)  +  " full " + to_string(fullCount));

    string message = Q.front();
    Q.pop();

    queueMutex.release();
    queueEmpty.release();

    return message;
}

void Print(string message) {
    outputMutex.acquire();
    cout << message << endl;
    outputMutex.release();
}

void Consumer(int id) {
   bool done{false};
   string message;

   while(not done) {
      Print("\t" + to_string(id) + "\t is sleeping ");
      usleep(rand() % 1000);


      Print("\t" + to_string(id) + "\t is getting a message ");
      message = GetMessage(id);
      if (message == "done") {
         done = true;
         Print("\t" + to_string(id) + "\t is exiting");
      } else {
         Print("\t" + to_string(id) + "\t got \"" + message + '"' );
      }
   }
}

void  PostMessage(string message, int id) {
    queueEmpty.acquire();
    queueMutex.acquire();
    Q.push(message);

    emptyCount --;
    fullCount ++;
    Print(to_string(id) + "\t\tPosted a message, empty " 
          + to_string(emptyCount) +  " full " + to_string(fullCount));

    queueMutex.release();
    queueFull.release();
}


void Producer(int id, int iterations) {
    string message;

    for(int i = 0; i < iterations; ++i) {
      Print(to_string(id) + " is sleeping ");
      usleep(rand() % 500);
  
      message = "message " + to_string(i+1) + " from " + to_string(id);
      Print("\t" + to_string(id) + " is posting a message ");
      PostMessage(message, id);
    }
    Print("\t" + to_string(id) + " is all done, byby");
}