aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--lib/Makefile7
-rw-r--r--src/Makefile7
-rw-r--r--src/sched.hpp8
-rw-r--r--src/sched/roundrobin.cpp21
-rw-r--r--src/task.h78
-rw-r--r--src/tst/roundrobin.cc50
-rw-r--r--src/tst/taskqueue.cc122
8 files changed, 296 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index fd9cc74..fa37164 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,11 @@ test:
@grep -rn 'asm' devices lib src
@echo Running tests
@make -C lib test
+ @make -C src test
+
+valgrind:
+ @make -C lib valgrind
+ @make -C src valgrind
FORCE:
diff --git a/lib/Makefile b/lib/Makefile
index b3e1f66..b82d773 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,11 +1,14 @@
include ../Makefile.config
libk.SRCS = stdio/printf.c stdio/fprintf.c stdio/vfprintf.cpp \
- stdlib/memcpy.c stdlib/memset.c \
+ stdlib/memcpy.c stdlib/memset.c stdlib/linked_list_allocator.c \
string/itoa.c
include ../rules.mk
-.PHONY: test
+.PHONY: test valgrind
test: tst/test_mem tst/test_string tst/test_linked_list_allocator
$(foreach f,$^,./$f;)
+
+valgrind: tst/test_mem tst/test_string tst/test_linked_list_allocator
+ $(foreach f,$^,valgrind --leak-check=full ./$f;)
diff --git a/src/Makefile b/src/Makefile
index 8f7c47c..df59f24 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -5,6 +5,13 @@ kernel.OBJS := include conf.h
include ../rules.mk
+.PHONY: test valgrind
+test: tst/test_taskqueue tst/test_roundrobin
+ $(foreach f,$^,./$f;)
+
+valgrind: tst/test_taskqueue tst/test_roundrobin
+ $(foreach f,$^,valgrind --leak-check=full ./$f;)
+
conf.h: conf.h.in
@echo ' GEN $@'
@cp conf.h.in conf.h
diff --git a/src/sched.hpp b/src/sched.hpp
new file mode 100644
index 0000000..cfa3ff0
--- /dev/null
+++ b/src/sched.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "task.h"
+
+class RoundRobinQueue : public Queue<Task> {
+public:
+ [[nodiscard]] Task *next(int slice);
+};
diff --git a/src/sched/roundrobin.cpp b/src/sched/roundrobin.cpp
new file mode 100644
index 0000000..c3d6cb6
--- /dev/null
+++ b/src/sched/roundrobin.cpp
@@ -0,0 +1,21 @@
+#include "../sched.hpp"
+
+/// Each task is run for a time quantum or the remainder of its cpu burst
+Task *
+RoundRobinQueue::next(int slice)
+{
+ if (head == nullptr) return nullptr;
+ if (head->node->burst <= 0) {
+ delete head->node;
+ remove(head->node);
+ return next(slice);
+ }
+
+ auto *it = head;
+ it->node->burst -= slice;
+ if (head->next) head = head->next;
+ it->next = nullptr;
+ tail->next = it;
+ tail = it;
+ return it->node;
+}
diff --git a/src/task.h b/src/task.h
new file mode 100644
index 0000000..381f628
--- /dev/null
+++ b/src/task.h
@@ -0,0 +1,78 @@
+#pragma once
+
+/**
+ * Representation of a task in the system
+ */
+struct Task {
+ const char *name;
+ int id;
+ int priority;
+ int burst;
+};
+
+#ifdef __cplusplus
+template <typename T> struct Queue {
+ struct Item {
+ Item(T *node) : node(node) {}
+ T *node;
+ Item *next = nullptr;
+
+ [[nodiscard]] bool
+ operator==(const T *other) const
+ {
+ return node == other;
+ }
+ };
+
+ ~Queue() noexcept
+ {
+ for (auto *it = head; it != nullptr;) {
+ auto *current = it;
+ it = it->next;
+ delete current->node;
+ delete current;
+ }
+ }
+
+ /// Insert item at the end of the queue
+ void
+ insert(T *item)
+ {
+ if (head == nullptr) {
+ head = new Item(item);
+ tail = head;
+ }
+ else {
+ tail->next = new Item(item);
+ tail = tail->next;
+ }
+ }
+
+ void
+ remove(T *item)
+ {
+ if (head == nullptr) return;
+ if (item == head->node) {
+ auto *it = head;
+ head = head->next;
+ if (*tail == item) tail = nullptr;
+ delete it;
+ return;
+ }
+
+ Item *prev = nullptr;
+ for (auto *it = head; it != nullptr; it = it->next) {
+ if (it->node == item) {
+ if (prev) { prev->next = it->next; }
+ if (tail == it) { tail = prev; }
+ delete it;
+ return;
+ }
+ prev = it;
+ }
+ }
+
+ Item *head = nullptr;
+ Item *tail = nullptr;
+};
+#endif
diff --git a/src/tst/roundrobin.cc b/src/tst/roundrobin.cc
new file mode 100644
index 0000000..1431788
--- /dev/null
+++ b/src/tst/roundrobin.cc
@@ -0,0 +1,50 @@
+#include <chrono>
+#include <gtest/gtest.h>
+#include <iomanip>
+#include <iostream>
+#include <valgrind/valgrind.h>
+
+#include "../sched/roundrobin.cpp"
+
+void
+run(Task *task, int slice)
+{
+ std::cout << "Running task " << task->name << " id=" << std::setw(2) << task->id << " prio=" << std::setw(2)
+ << task->priority << " burst=" << std::setw(2) << task->burst << " slice=" << slice << " ";
+}
+
+struct DebugRoundRobinQueue : public RoundRobinQueue {
+public:
+ void
+ print() const
+ {
+ for (auto *it = head; it != nullptr; it = it->next) {
+ std::cout << it->node->name << '(' << std::setw(2) << it->node->burst << ") ";
+ }
+ std::cout << std::endl;
+ }
+};
+
+TEST(roundrobin, RoundRobinQueue)
+{
+ DebugRoundRobinQueue queue;
+ queue.insert(new Task{"P1", 1, 1, 50});
+ queue.insert(new Task{"P2", 2, 1, 40});
+ queue.insert(new Task{"P3", 3, 1, 50});
+ queue.insert(new Task{"P4", 4, 1, 40});
+
+ const auto begin = std::chrono::system_clock::now();
+ for (auto *t = queue.next(10); t != nullptr; t = queue.next(10)) {
+ run(t, 10);
+ queue.print();
+ }
+ const auto end = std::chrono::system_clock::now();
+ const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
+
+ std::cout << "Completed in (us): " << duration << std::endl;
+ // test should complete in 250us unless running on valgrind
+ if (!RUNNING_ON_VALGRIND) EXPECT_LE(duration, 250);
+
+ EXPECT_EQ(queue.head, nullptr);
+ EXPECT_EQ(queue.tail, nullptr);
+}
diff --git a/src/tst/taskqueue.cc b/src/tst/taskqueue.cc
new file mode 100644
index 0000000..217c44d
--- /dev/null
+++ b/src/tst/taskqueue.cc
@@ -0,0 +1,122 @@
+#include <gtest/gtest.h>
+
+#include "../sched.hpp"
+
+struct DebugQueue : public Queue<Task> {
+public:
+ void
+ expect_ordered() const
+ {
+ int id = 1;
+ for (auto *it = head; it != nullptr; it = it->next) {
+ // std::cout << it->node->name << std::endl;
+ EXPECT_EQ(it->node->id, id++);
+ }
+ }
+};
+
+TEST(taskqueue, insert)
+{
+ DebugQueue queue;
+ auto *p1 = new Task{"P1", 1, 1, 10};
+ queue.insert(p1);
+ queue.insert(new Task{"P2", 2, 1, 10});
+ queue.insert(new Task{"P3", 3, 1, 10});
+ auto *p4 = new Task{"P4", 4, 1, 10};
+ queue.insert(p4);
+ queue.expect_ordered();
+
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p4);
+}
+
+TEST(taskqueue, removeHead)
+{
+ DebugQueue queue;
+ auto *p0 = new Task{"P0", 0, 1, 10};
+ queue.insert(p0);
+ auto *p1 = new Task{"P1", 1, 1, 10};
+ queue.insert(p1);
+ queue.insert(new Task{"P2", 2, 1, 10});
+ queue.insert(new Task{"P3", 3, 1, 10});
+ auto *p4 = new Task{"P4", 4, 1, 10};
+ queue.insert(p4);
+ queue.remove(p0);
+ delete p0;
+
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p4);
+ queue.expect_ordered();
+}
+
+TEST(taskqueue, removeTail)
+{
+ DebugQueue queue;
+ auto *p1 = new Task{"P1", 1, 1, 10};
+ queue.insert(p1);
+ queue.insert(new Task{"P2", 2, 1, 10});
+ queue.insert(new Task{"P3", 3, 1, 10});
+ auto *p4 = new Task{"P4", 4, 1, 10};
+ queue.insert(p4);
+ auto *p5 = new Task{"P5", 5, 1, 10};
+ queue.insert(p5);
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p5);
+
+ queue.remove(p5);
+ delete p5;
+
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p4);
+ queue.expect_ordered();
+}
+
+TEST(taskqueue, removeLast)
+{
+ DebugQueue queue;
+
+ auto *p0 = new Task{"P0", 0, 1, 10};
+ queue.insert(p0);
+ EXPECT_EQ(queue.head->node, p0);
+ EXPECT_EQ(queue.tail->node, p0);
+
+ queue.remove(p0);
+ delete p0;
+
+ EXPECT_EQ(queue.head, nullptr);
+ EXPECT_EQ(queue.tail, nullptr);
+}
+
+TEST(taskqueue, removeNullptr)
+{
+ DebugQueue queue;
+ queue.insert(new Task{"P1", 1, 1, 10});
+ queue.insert(new Task{"P2", 2, 1, 10});
+ queue.insert(new Task{"P3", 3, 1, 10});
+ queue.insert(new Task{"P4", 4, 1, 10});
+ queue.remove(nullptr);
+ queue.expect_ordered();
+}
+
+TEST(taskqueue, remove)
+{
+ DebugQueue queue;
+ auto *p1 = new Task{"P1", 1, 1, 10};
+ queue.insert(p1);
+ queue.insert(new Task{"P2", 2, 1, 10});
+ auto *p0 = new Task{"P0", 0, 1, 10};
+ queue.insert(p0);
+ queue.insert(new Task{"P3", 3, 1, 10});
+ auto *p4 = new Task{"P4", 4, 1, 10};
+ queue.insert(p4);
+
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p4);
+
+ queue.remove(p0);
+ delete p0;
+
+ EXPECT_EQ(queue.head->node, p1);
+ EXPECT_EQ(queue.tail->node, p4);
+ queue.expect_ordered();
+}