summaryrefslogtreecommitdiff
path: root/os2w3
diff options
context:
space:
mode:
authorlonkaars <loek@pipeframe.xyz>2023-05-13 12:45:37 +0200
committerlonkaars <loek@pipeframe.xyz>2023-05-13 12:45:37 +0200
commite95bd56df67f3ba6d6f9d44c66b791e45bcd54a3 (patch)
tree457276154858c7fe71a111842b1d377bbcc25bcc /os2w3
parent17dd9cdc6d5beb636695e5becbc93275524665af (diff)
week 3 worky
Diffstat (limited to 'os2w3')
-rw-r--r--os2w3/.gitignore2
-rw-r--r--os2w3/main.cpp130
-rw-r--r--os2w3/makefile17
3 files changed, 149 insertions, 0 deletions
diff --git a/os2w3/.gitignore b/os2w3/.gitignore
new file mode 100644
index 0000000..f0c9b81
--- /dev/null
+++ b/os2w3/.gitignore
@@ -0,0 +1,2 @@
+*.o
+main
diff --git a/os2w3/main.cpp b/os2w3/main.cpp
new file mode 100644
index 0000000..5574fbc
--- /dev/null
+++ b/os2w3/main.cpp
@@ -0,0 +1,130 @@
+// beetje rommelig om <stdlib.h> te gebruiken inplaats van <cstdlib> in c++ :(
+// verder worden ze ook helemaal niet gebruikt dus ik weet niet waarom het hier staat
+// #include <cstdlib>
+// #include <cstdio>
+// #include <ctime>
+#include <iostream>
+#include <random>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+using namespace std;
+
+const int TICK = 1000;
+const int NBOXES = 10;
+const int BUFLEN = 4;
+
+// https://stackoverflow.com/questions/21237905/how-do-i-generate-thread-safe-uniform-random-numbers
+int random(int n) {
+ static thread_local std::mt19937 generator;
+ std::uniform_int_distribution<int> distribution(0, n);
+ return distribution(generator);
+}
+
+class Box {
+private:
+ const char *name;
+ int number;
+public:
+ Box(const char *name, int number) {
+ this->name = name;
+ this->number = number;
+ }
+ friend ostream &operator << (ostream &out, Box *b) {
+ return out << b->name << "-" << b->number;
+ }
+};
+
+class Queue {
+private:
+ Box *buffer[BUFLEN] = { nullptr };
+
+ int getpos = 0, putpos = 0;
+ int count = 0;
+
+ mutex _busy;
+ // deze mutex zorgt ervoor dat alle variabelen binnen de critical section
+ // (`buffer`, `getpos`, `putpos`, `count`, `_can_get`, en `_can_put`)
+ // allemaal door maar één thread tegelijkertijd aangepast kunnen worden,
+ // waardoor deze klasse een thread-safe queue implementeert
+ condition_variable _can_get;
+ condition_variable _can_put;
+
+public:
+ Queue() { }
+ // initialisatiewaarden van private variabelen bij de declaratie neergezet,
+ // de std library mutex en unique_lock hebben geen expliciete initialisatie
+ // nodig
+
+ Box *get(const char *consumername) {
+ unique_lock<mutex> lock(_busy);
+ // deze mutex komt overeen met de windows api code die aan het begin van
+ // een functie EnterCriticalSection doet, en aan het einde
+ // LeaveCriticalSection (maar dit gebeurt impliciet omdat de variabele
+ // `lock` buiten de scope valt bij de return van deze functie)
+ while (count == 0) // wacht tot er minstens een doos is om te pakken
+ _can_get.wait(lock); // wacht tot een melding op _can_get (vermijd CPU kachel)
+ // de bovenstaande while loop met wait gedoe is gelijk aan WaitForSingleObject
+ Box *box = buffer[getpos];
+ getpos = (getpos + 1) % BUFLEN;
+ count--;
+ cout << consumername << ": gets " << box << endl;
+ _can_put.notify_one(); // meld aan threads die wachten op _can_put dat
+ // count anders is geworden
+ return box;
+ }
+
+ // dezelfde structuur wordt gebruikt voor thread safety als de bovenstaande
+ // functie, maar dan met _can_put en _can_get omgedraaid
+ void put(const char *producername, Box *box) {
+ unique_lock<mutex> lock(_busy);
+ while (count == BUFLEN) _can_put.wait(lock);
+ cout << producername << ": puts " << box << endl;
+ buffer[putpos] = box;
+ putpos = (putpos + 1) % BUFLEN;
+ count++;
+ _can_get.notify_one();
+ }
+};
+
+Queue q;
+
+// thread functie hoeft verder niks te returnen (waarom DWORD WINAPI ??)
+void produce(void *arg) {
+ char *name = (char*) arg;
+ for(int i = 0; i < NBOXES; i++) {
+ // c++ stdlib sleep :tada:
+ std::this_thread::sleep_for(std::chrono::milliseconds(random(TICK)));
+ Box *box = new Box(name, i);
+ q.put(name, box);
+ }
+}
+
+void consume(void *arg) {
+ char *name = (char*) arg;
+ for(int i = 0; i < NBOXES; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(random(TICK)));
+ Box *box = q.get(name);
+ delete box;
+ }
+}
+
+int main(int argc, char* argv[]) {
+ // c++ stdlib threads :tada:
+ vector<thread> pool;
+
+ pool.push_back(thread(produce, (void*)"P1"));
+ pool.push_back(thread(produce, (void*)"P2"));
+ pool.push_back(thread(produce, (void*)"P3"));
+ pool.push_back(thread(produce, (void*)"P4"));
+
+ pool.push_back(thread(consume, (void*)"C1"));
+ pool.push_back(thread(consume, (void*)"C2"));
+ pool.push_back(thread(consume, (void*)"C3"));
+ pool.push_back(thread(consume, (void*)"C4"));
+
+ for (thread& t : pool) t.join();
+
+ return 0;
+}
+
diff --git a/os2w3/makefile b/os2w3/makefile
new file mode 100644
index 0000000..258d688
--- /dev/null
+++ b/os2w3/makefile
@@ -0,0 +1,17 @@
+CPP = g++
+LD = g++
+
+LFLAGS += -lstdc++
+CFLAGS += -g
+
+all: main
+
+main: main.o
+ $(LD) $^ $(LFLAGS) -o $@
+
+%.o: %.cpp
+ $(CPP) -c $(CFLAGS) $< -o $@
+
+clean:
+ $(RM) main.o main
+