diff --git a/src/kernel/process/Scheduler.cc b/src/kernel/process/Scheduler.cc index 357a860..f645426 100644 --- a/src/kernel/process/Scheduler.cc +++ b/src/kernel/process/Scheduler.cc @@ -10,13 +10,13 @@ Scheduler::Scheduler() { // TODO: Cleanup thread } -void Scheduler::ready(Memory::unique_ptr &&thread) { +void Scheduler::ready(Memory::UniquePtr &&thread) { Device::CPU::disable_int(); ready_queue.push_back(std::move(thread)); Device::CPU::enable_int(); } -void Scheduler::start(Container::Vector>::iterator next) { +void Scheduler::start(Container::Vector>::iterator next) { active = next; if (active >= ready_queue.end()) { // By jumping to the start of the queue we can always start the next ready thread by @@ -28,7 +28,7 @@ void Scheduler::start(Container::Vector>::iterator ne // Use a raw pointer for the previous thread because the unique_ptr of that thread is still in the ready_queue, // this way we don't need to take care of moving it to this function and then back to the ready_queue -void Scheduler::start(Thread *prev_raw, Container::Vector>::iterator next) { +void Scheduler::start(Thread *prev_raw, Container::Vector>::iterator next) { active = next; if (active >= ready_queue.end()) { active = ready_queue.begin(); @@ -65,7 +65,7 @@ void Scheduler::block() { void Scheduler::deblock(uint16_t tid) { Device::CPU::disable_int(); - for (Container::Vector>::iterator it = block_queue.begin(); + for (Container::Vector>::iterator it = block_queue.begin(); it != block_queue.end(); ++it) { if ((*it)->tid == tid) { // Found thread with correct tid @@ -98,7 +98,7 @@ void Scheduler::exit() { // dispatch kehrt nicht zurueck } -void Scheduler::kill(uint16_t tid, Memory::unique_ptr *ptr) { +void Scheduler::kill(uint16_t tid, Memory::UniquePtr *ptr) { Device::CPU::disable_int(); uint16_t prev_tid = (*active)->tid; @@ -128,7 +128,7 @@ void Scheduler::kill(uint16_t tid, Memory::unique_ptr *ptr) { return; } - for (Container::Vector>::iterator it = ready_queue.begin(); + for (Container::Vector>::iterator it = ready_queue.begin(); it != ready_queue.end(); ++it) { if ((*it)->tid == tid) { // Found thread to kill @@ -163,10 +163,10 @@ void Scheduler::kill(uint16_t tid) { // TODO: Can't retrieve the thread right now because it's not clear when it's finished, // maybe introduce a exited_queue and get it from there -void Scheduler::nice_kill(uint16_t tid, Memory::unique_ptr *ptr) { +void Scheduler::nice_kill(uint16_t tid, Memory::UniquePtr *ptr) { Device::CPU::disable_int(); - for (Memory::unique_ptr &thread: block_queue) { + for (Memory::UniquePtr &thread: block_queue) { if (thread->tid == tid) { thread->suicide(); deblock(tid); @@ -175,7 +175,7 @@ void Scheduler::nice_kill(uint16_t tid, Memory::unique_ptr *ptr) { } } - for (Memory::unique_ptr &thread: ready_queue) { + for (Memory::UniquePtr &thread: ready_queue) { if (thread->tid == tid) { thread->suicide(); Device::CPU::enable_int(); diff --git a/src/kernel/process/Scheduler.h b/src/kernel/process/Scheduler.h index cb4794b..5a5cc34 100644 --- a/src/kernel/process/Scheduler.h +++ b/src/kernel/process/Scheduler.h @@ -32,7 +32,7 @@ private: * * @param thread The thread to add to the ready_queue */ - void ready(Memory::unique_ptr &&thread); + void ready(Memory::UniquePtr &&thread); /** * Start/continue a thread independently of the previous running thread. @@ -40,7 +40,7 @@ private: * * @param next The next thread to run */ - void start(Container::Vector>::iterator next); + void start(Container::Vector>::iterator next); /** * Start/continue a thread. @@ -49,7 +49,7 @@ private: * @param prev_raw The raw pointer to the previous running thread * @param next The next thread to run */ - void start(Thread *prev_raw, Container::Vector>::iterator next); + void start(Thread *prev_raw, Container::Vector>::iterator next); /** * Give CPU time to the next waiting thread from the ready_queue. @@ -81,7 +81,7 @@ private: * @param tid The thread to exit * @param ptr The pointer to the killed thread for external use */ - void kill(uint16_t tid, Memory::unique_ptr *ptr); + void kill(uint16_t tid, Memory::UniquePtr *ptr); /** * Forcefully exit a thread and discard it. @@ -96,19 +96,19 @@ private: // or were involved in any locking mechanisms, so with this a thread can make sure // to "set things right" before exiting itself (but could also be ignored) // TODO: "Setting things right" should be done in the thread's destructor - void nice_kill(uint16_t tid, Memory::unique_ptr *ptr); + void nice_kill(uint16_t tid, Memory::UniquePtr *ptr); void nice_kill(uint16_t tid); private: - Container::Vector> ready_queue; - Container::Vector> block_queue; - Container::Vector> exit_queue; // TODO: Manage exited threads + Container::Vector> ready_queue; + Container::Vector> block_queue; + Container::Vector> exit_queue; // TODO: Manage exited threads // It makes sense to keep track of the active thread through this as it makes handling the // unique_ptr easier and reduces the copying in the vector when cycling through the threads // as we don't have to keep the active thread at the front (would only make sense with a queue) - Container::Vector>::iterator active = nullptr; + Container::Vector>::iterator active = nullptr; // TODO: Synchronization }; diff --git a/src/kernel/service/SchedulerService.h b/src/kernel/service/SchedulerService.h index 4da3663..b1db510 100644 --- a/src/kernel/service/SchedulerService.h +++ b/src/kernel/service/SchedulerService.h @@ -34,7 +34,7 @@ public: */ template uint32_t ready(Args... args) { - Memory::unique_ptr thread = Memory::make_unique(std::forward(args)...); + Memory::UniquePtr thread = Memory::make_unique(std::forward(args)...); uint32_t tid = thread->tid; scheduler.ready(std::move(thread)); diff --git a/src/lib/demo/SmartPointerDemo.cc b/src/lib/demo/SmartPointerDemo.cc index ad87732..60cc323 100644 --- a/src/lib/demo/SmartPointerDemo.cc +++ b/src/lib/demo/SmartPointerDemo.cc @@ -12,15 +12,15 @@ void SmartPointerDemo::run() { { log.info() << "Allocating new unique_ptr..." << endl; - Memory::unique_ptr ptr = Memory::make_unique(1); + Memory::UniquePtr ptr = Memory::make_unique(1); log.info() << "Leaving scope..." << endl; } log.info() << "Should be deleted by now..." << endl; { log.info() << "Allocating new unique_ptr..." << endl; - Memory::unique_ptr ptr1 = Memory::make_unique(1); - Memory::unique_ptr ptr2; + Memory::UniquePtr ptr1 = Memory::make_unique(1); + Memory::UniquePtr ptr2; log.info() << "*ptr1 == " << *ptr1 << ", (bool)ptr2 == " << static_cast(ptr2) << endl; log.info() << "Moving ptr1 => ptr2 (no allocations should happen)..." << endl; @@ -33,8 +33,8 @@ void SmartPointerDemo::run() { { log.info() << "Allocating (2) new unique_ptr..." << endl; - Memory::unique_ptr ptr1 = Memory::make_unique(1); - Memory::unique_ptr ptr2 = Memory::make_unique(1); + Memory::UniquePtr ptr1 = Memory::make_unique(1); + Memory::UniquePtr ptr2 = Memory::make_unique(1); log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl; ptr2 = std::move(ptr1); @@ -47,7 +47,7 @@ void SmartPointerDemo::run() { { log.info() << "Allocating new unique_ptr..." << endl; - Memory::unique_ptr ptr = Memory::make_unique(10); + Memory::UniquePtr ptr = Memory::make_unique(10); ptr[0] = 1; log.info() << "ptr[0] == " << ptr[0] << endl; } @@ -55,8 +55,8 @@ void SmartPointerDemo::run() { { log.info() << "Allocating new unique_ptr..." << endl; - Memory::unique_ptr ptr1 = Memory::make_unique(10); - Memory::unique_ptr ptr2; + Memory::UniquePtr ptr1 = Memory::make_unique(10); + Memory::UniquePtr ptr2; log.info() << "Moving ptr1 => ptr2 (no allocations should happen)..." << endl; ptr2 = std::move(ptr1); @@ -67,8 +67,8 @@ void SmartPointerDemo::run() { { log.info() << "Allocating (2) new unique_ptr..." << endl; - Memory::unique_ptr ptr1 = Memory::make_unique(10); - Memory::unique_ptr ptr2 = Memory::make_unique(10); + Memory::UniquePtr ptr1 = Memory::make_unique(10); + Memory::UniquePtr ptr2 = Memory::make_unique(10); log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl; ptr2 = std::move(ptr1); @@ -79,7 +79,7 @@ void SmartPointerDemo::run() { // NOTE: This wasn't working because of a missing operator[] delete in the allocator log.info() << "Allocating unique_ptr*..." << endl; - Memory::unique_ptr *ptrptr = new Memory::unique_ptr[10]; + Memory::UniquePtr *ptrptr = new Memory::UniquePtr[10]; delete[] ptrptr; log.info() << "Should be deleted by now..." << endl; @@ -87,7 +87,7 @@ void SmartPointerDemo::run() { { log.info() << "Stackallocating Array, 10>..." << endl; - Container::Array, 10> arr; + Container::Array, 10> arr; log.info() << "Populating slot 0..." << endl; arr[0] = Memory::make_unique(1); log.info() << "Moving slot 0 to slot 1..." << endl; @@ -98,7 +98,7 @@ void SmartPointerDemo::run() { { log.info() << "Heapallocating Array, 10>..." << endl; - Container::Array, 10> *arr = new Container::Array, 10>; + Container::Array, 10> *arr = new Container::Array, 10>; log.info() << "Populating slot 0..." << endl; (*arr)[0] = Memory::make_unique(1); log.info() << "Moving slot 0 to slot 1..." << endl; @@ -111,7 +111,7 @@ void SmartPointerDemo::run() { { log.info() << "ArrayList>..." << endl; - Container::Vector> vec; + Container::Vector> vec; log.info() << "2x insertion" << endl; vec.push_back(Memory::make_unique(1)); vec.push_back(Memory::make_unique(2)); diff --git a/src/lib/memory/Memory.h b/src/lib/memory/Memory.h index 85e7903..92cccf7 100755 --- a/src/lib/memory/Memory.h +++ b/src/lib/memory/Memory.h @@ -2,24 +2,23 @@ #define MYSTDLIB_INCLUDE_H_ #include +#include namespace Memory { - // add using byte or sth to replace char - - template - void memcpy(T* destination, const T* source, std::size_t count = 1) { - for (unsigned int i = 0; i < count; ++i) { - *(destination + i) = *(source + i); - } +template +void memcpy(T *destination, const T *source, std::size_t count = 1) { + for (unsigned int i = 0; i < count; ++i) { + *(destination + i) = *(source + i); } +} - void memset(char* destination, char value, std::size_t bytes); +void memset(uint8_t *destination, uint8_t value, std::size_t bytes); - template - void zero(T* destination) { - memset(reinterpret_cast(destination), '\0', sizeof(T)); - } +template +void zero(T *destination) { + memset(reinterpret_cast(destination), '\0', sizeof(T)); +} } // namespace bse diff --git a/src/lib/memory/UniquePointer.h b/src/lib/memory/UniquePointer.h index d488e43..ed49797 100644 --- a/src/lib/memory/UniquePointer.h +++ b/src/lib/memory/UniquePointer.h @@ -2,6 +2,7 @@ #define UniquePointer_Include_H_ #include +#include "lib/util/RestrictedConstructors.h" // https://en.cppreference.com/w/cpp/memory/unique_ptr @@ -14,75 +15,71 @@ namespace Memory { // T is the type make_unique is called with, meaning int or int[] for example // T_ is the bare type without extents (int in both cases), so we have a // int* pointer type for both unique_ptr and unique_ptr + +/** + * This class implements a simple managed pointer with a single owner. + * + * @tparam T The type of object the pointer holds + */ template -class unique_ptr { -private: +class UniquePtr { using T_ = std::remove_extent_t; - T_ *ptr = nullptr; - - // TODO: Was there a reason I didn't mark this explicit? - // Only use make_unique or reset for construction - unique_ptr(T_ *ptr) : ptr(ptr) {} - - // I didn't want to introduce custom deleters for my small needs - void del() { - if constexpr (std::is_array_v) { - delete[] ptr; - } else { - delete ptr; - } - ptr = nullptr; - } - public: - // Forbid copying - unique_ptr(const unique_ptr ©) = delete; - - unique_ptr &operator=(const unique_ptr ©) = delete; - // Construction - unique_ptr() = default; // Allow declaration without explicit definition + UniquePtr() = default; // Allow declaration without explicit definition - template - friend typename std::enable_if_t, unique_ptr> - make_unique(Args &&... args); - - template - friend typename std::enable_if_t, unique_ptr> - make_unique(std::size_t size); - - // Deletion - ~unique_ptr() { - del(); - } + MakeUncopyable(UniquePtr) // Moving - unique_ptr(unique_ptr &&move) noexcept { reset(move.release()); }; + UniquePtr(UniquePtr &&move) noexcept { reset(move.release()); }; - // Implicit upcasting is needed: for sth like - // unique_ptr ptr = make_unique(); - // IdleThread is derived from Thread so the assert passes + // Implicit upcasting is needed: unique_ptr ptr = make_unique(); + // Not explicit for the same reason template - unique_ptr(unique_ptr &&move) noexcept { + UniquePtr(UniquePtr &&move) noexcept { // NOLINT(google-explicit-constructor) static_assert(std::is_base_of_v, "Has to be derived type"); reset(move.release()); } - unique_ptr &operator=(unique_ptr &&move) noexcept { + UniquePtr &operator=(UniquePtr &&move) noexcept { reset(move.release()); return *this; } - // Resetting: Replaces managed object, deleting the old one + ~UniquePtr() { + del(); + } + + // Utility construction + template + friend typename std::enable_if_t, UniquePtr> + make_unique(Args &&... args); + + template + friend typename std::enable_if_t, UniquePtr> + make_unique(std::size_t size); + + /** + * Delete the contained object and reset the pointer. + */ void reset() { del(); } + /** + * Delete the contained object and reset the pointer to a new pointer. + * + * @param pt The new pointer stored in the unique pointer + */ void reset(T_ *pt) { del(); ptr = pt; } - // Release: Releases ownership without deletion + /** + * Release the contained object without deleting it. + * + * @return The contained pointer + */ T_ *release() { // T* old = ptr; // ptr = nullptr; @@ -90,7 +87,11 @@ public: return std::exchange(ptr, nullptr); } - // Get: Access the raw pointer without taking ownership + /** + * Get a pointer to the contained object without taking ownership. + * + * @return The contained pointer + */ T_ *get() const { return ptr; } @@ -108,12 +109,31 @@ public: explicit operator bool() const { return (ptr != nullptr); } - bool operator==(const unique_ptr &other) const { return ptr == other.ptr; } + bool operator==(const UniquePtr &other) const { return ptr == other.ptr; } // These are only for array unique_ptr but I didn't enforce that T_ &operator[](std::size_t i) { return ptr[i]; } const T_ &operator[](std::size_t i) const { return ptr[i]; } + +private: + T_ *ptr = nullptr; + +private: + // Only use make_unique or reset for construction + // Not explicit to allow assigning nullptr: UniquePtr ptr = nullptr; + UniquePtr(T_ *ptr) : ptr(ptr) {} // NOLINT(google-explicit-constructor) + + // I didn't want to introduce custom deleters for my small needs + void del() { + if constexpr (std::is_array_v) { + delete[] ptr; + } else { + delete ptr; + } + ptr = nullptr; + } + }; // make_unique implementation ======================================= @@ -123,16 +143,16 @@ public: template // We make the return type dependent on whether T is an array type or not -typename std::enable_if_t, unique_ptr> +typename std::enable_if_t, UniquePtr> make_unique(Args &&... args) { - return unique_ptr(new T(std::forward(args)...)); + return UniquePtr(new T(std::forward(args)...)); } template -typename std::enable_if_t, unique_ptr> +typename std::enable_if_t, UniquePtr> make_unique(std::size_t size) { using T_ = typename std::remove_extent_t; - return unique_ptr(new T_[size]); + return UniquePtr(new T_[size]); } } // namespace bse