diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | lib/Makefile | 7 | ||||
-rw-r--r-- | src/Makefile | 7 | ||||
-rw-r--r-- | src/sched.hpp | 8 | ||||
-rw-r--r-- | src/sched/roundrobin.cpp | 21 | ||||
-rw-r--r-- | src/task.h | 78 | ||||
-rw-r--r-- | src/tst/roundrobin.cc | 50 | ||||
-rw-r--r-- | src/tst/taskqueue.cc | 122 |
8 files changed, 296 insertions, 2 deletions
@@ -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(); +} |