1

Cleanup memory classes

This commit is contained in:
2022-12-08 22:23:08 +01:00
parent 78a46fb6fd
commit 925b0f9b3e
6 changed files with 114 additions and 95 deletions

View File

@ -10,13 +10,13 @@ Scheduler::Scheduler() {
// TODO: Cleanup thread // TODO: Cleanup thread
} }
void Scheduler::ready(Memory::unique_ptr<Thread> &&thread) { void Scheduler::ready(Memory::UniquePtr<Thread> &&thread) {
Device::CPU::disable_int(); Device::CPU::disable_int();
ready_queue.push_back(std::move(thread)); ready_queue.push_back(std::move(thread));
Device::CPU::enable_int(); Device::CPU::enable_int();
} }
void Scheduler::start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next) { void Scheduler::start(Container::Vector<Memory::UniquePtr<Thread>>::iterator next) {
active = next; active = next;
if (active >= ready_queue.end()) { if (active >= ready_queue.end()) {
// By jumping to the start of the queue we can always start the next ready thread by // 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<Memory::unique_ptr<Thread>>::iterator ne
// Use a raw pointer for the previous thread because the unique_ptr of that thread is still in the ready_queue, // 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 // 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<Memory::unique_ptr<Thread>>::iterator next) { void Scheduler::start(Thread *prev_raw, Container::Vector<Memory::UniquePtr<Thread>>::iterator next) {
active = next; active = next;
if (active >= ready_queue.end()) { if (active >= ready_queue.end()) {
active = ready_queue.begin(); active = ready_queue.begin();
@ -65,7 +65,7 @@ void Scheduler::block() {
void Scheduler::deblock(uint16_t tid) { void Scheduler::deblock(uint16_t tid) {
Device::CPU::disable_int(); Device::CPU::disable_int();
for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = block_queue.begin(); for (Container::Vector<Memory::UniquePtr<Thread>>::iterator it = block_queue.begin();
it != block_queue.end(); ++it) { it != block_queue.end(); ++it) {
if ((*it)->tid == tid) { if ((*it)->tid == tid) {
// Found thread with correct tid // Found thread with correct tid
@ -98,7 +98,7 @@ void Scheduler::exit() {
// dispatch kehrt nicht zurueck // dispatch kehrt nicht zurueck
} }
void Scheduler::kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr) { void Scheduler::kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr) {
Device::CPU::disable_int(); Device::CPU::disable_int();
uint16_t prev_tid = (*active)->tid; uint16_t prev_tid = (*active)->tid;
@ -128,7 +128,7 @@ void Scheduler::kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr) {
return; return;
} }
for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = ready_queue.begin(); for (Container::Vector<Memory::UniquePtr<Thread>>::iterator it = ready_queue.begin();
it != ready_queue.end(); ++it) { it != ready_queue.end(); ++it) {
if ((*it)->tid == tid) { if ((*it)->tid == tid) {
// Found thread to kill // 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, // 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 // maybe introduce a exited_queue and get it from there
void Scheduler::nice_kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr) { void Scheduler::nice_kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr) {
Device::CPU::disable_int(); Device::CPU::disable_int();
for (Memory::unique_ptr<Thread> &thread: block_queue) { for (Memory::UniquePtr<Thread> &thread: block_queue) {
if (thread->tid == tid) { if (thread->tid == tid) {
thread->suicide(); thread->suicide();
deblock(tid); deblock(tid);
@ -175,7 +175,7 @@ void Scheduler::nice_kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr) {
} }
} }
for (Memory::unique_ptr<Thread> &thread: ready_queue) { for (Memory::UniquePtr<Thread> &thread: ready_queue) {
if (thread->tid == tid) { if (thread->tid == tid) {
thread->suicide(); thread->suicide();
Device::CPU::enable_int(); Device::CPU::enable_int();

View File

@ -32,7 +32,7 @@ private:
* *
* @param thread The thread to add to the ready_queue * @param thread The thread to add to the ready_queue
*/ */
void ready(Memory::unique_ptr<Thread> &&thread); void ready(Memory::UniquePtr<Thread> &&thread);
/** /**
* Start/continue a thread independently of the previous running thread. * Start/continue a thread independently of the previous running thread.
@ -40,7 +40,7 @@ private:
* *
* @param next The next thread to run * @param next The next thread to run
*/ */
void start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next); void start(Container::Vector<Memory::UniquePtr<Thread>>::iterator next);
/** /**
* Start/continue a thread. * Start/continue a thread.
@ -49,7 +49,7 @@ private:
* @param prev_raw The raw pointer to the previous running thread * @param prev_raw The raw pointer to the previous running thread
* @param next The next thread to run * @param next The next thread to run
*/ */
void start(Thread *prev_raw, Container::Vector<Memory::unique_ptr<Thread>>::iterator next); void start(Thread *prev_raw, Container::Vector<Memory::UniquePtr<Thread>>::iterator next);
/** /**
* Give CPU time to the next waiting thread from the ready_queue. * Give CPU time to the next waiting thread from the ready_queue.
@ -81,7 +81,7 @@ private:
* @param tid The thread to exit * @param tid The thread to exit
* @param ptr The pointer to the killed thread for external use * @param ptr The pointer to the killed thread for external use
*/ */
void kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr); void kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr);
/** /**
* Forcefully exit a thread and discard it. * 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 // 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) // to "set things right" before exiting itself (but could also be ignored)
// TODO: "Setting things right" should be done in the thread's destructor // TODO: "Setting things right" should be done in the thread's destructor
void nice_kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr); void nice_kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr);
void nice_kill(uint16_t tid); void nice_kill(uint16_t tid);
private: private:
Container::Vector<Memory::unique_ptr<Thread>> ready_queue; Container::Vector<Memory::UniquePtr<Thread>> ready_queue;
Container::Vector<Memory::unique_ptr<Thread>> block_queue; Container::Vector<Memory::UniquePtr<Thread>> block_queue;
Container::Vector<Memory::unique_ptr<Thread>> exit_queue; // TODO: Manage exited threads Container::Vector<Memory::UniquePtr<Thread>> exit_queue; // TODO: Manage exited threads
// It makes sense to keep track of the active thread through this as it makes handling the // 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 // 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) // as we don't have to keep the active thread at the front (would only make sense with a queue)
Container::Vector<Memory::unique_ptr<Thread>>::iterator active = nullptr; Container::Vector<Memory::UniquePtr<Thread>>::iterator active = nullptr;
// TODO: Synchronization // TODO: Synchronization
}; };

View File

@ -34,7 +34,7 @@ public:
*/ */
template<typename T, typename... Args> template<typename T, typename... Args>
uint32_t ready(Args... args) { uint32_t ready(Args... args) {
Memory::unique_ptr<Thread> thread = Memory::make_unique<T>(std::forward<Args>(args)...); Memory::UniquePtr<Thread> thread = Memory::make_unique<T>(std::forward<Args>(args)...);
uint32_t tid = thread->tid; uint32_t tid = thread->tid;
scheduler.ready(std::move(thread)); scheduler.ready(std::move(thread));

View File

@ -12,15 +12,15 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Allocating new unique_ptr<int>..." << endl; log.info() << "Allocating new unique_ptr<int>..." << endl;
Memory::unique_ptr<int> ptr = Memory::make_unique<int>(1); Memory::UniquePtr<int> ptr = Memory::make_unique<int>(1);
log.info() << "Leaving scope..." << endl; log.info() << "Leaving scope..." << endl;
} }
log.info() << "Should be deleted by now..." << endl; log.info() << "Should be deleted by now..." << endl;
{ {
log.info() << "Allocating new unique_ptr<int>..." << endl; log.info() << "Allocating new unique_ptr<int>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(1); Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(1);
Memory::unique_ptr<int> ptr2; Memory::UniquePtr<int> ptr2;
log.info() << "*ptr1 == " << *ptr1 << ", (bool)ptr2 == " << static_cast<bool>(ptr2) << endl; log.info() << "*ptr1 == " << *ptr1 << ", (bool)ptr2 == " << static_cast<bool>(ptr2) << endl;
log.info() << "Moving ptr1 => ptr2 (no allocations should happen)..." << 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<int>..." << endl; log.info() << "Allocating (2) new unique_ptr<int>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(1); Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(1);
Memory::unique_ptr<int> ptr2 = Memory::make_unique<int>(1); Memory::UniquePtr<int> ptr2 = Memory::make_unique<int>(1);
log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl; log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl;
ptr2 = std::move(ptr1); ptr2 = std::move(ptr1);
@ -47,7 +47,7 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Allocating new unique_ptr<int[]>..." << endl; log.info() << "Allocating new unique_ptr<int[]>..." << endl;
Memory::unique_ptr<int[]> ptr = Memory::make_unique<int[]>(10); Memory::UniquePtr<int[]> ptr = Memory::make_unique<int[]>(10);
ptr[0] = 1; ptr[0] = 1;
log.info() << "ptr[0] == " << ptr[0] << endl; log.info() << "ptr[0] == " << ptr[0] << endl;
} }
@ -55,8 +55,8 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Allocating new unique_ptr<int[10]>..." << endl; log.info() << "Allocating new unique_ptr<int[10]>..." << endl;
Memory::unique_ptr<int[]> ptr1 = Memory::make_unique<int[]>(10); Memory::UniquePtr<int[]> ptr1 = Memory::make_unique<int[]>(10);
Memory::unique_ptr<int[]> ptr2; Memory::UniquePtr<int[]> ptr2;
log.info() << "Moving ptr1 => ptr2 (no allocations should happen)..." << endl; log.info() << "Moving ptr1 => ptr2 (no allocations should happen)..." << endl;
ptr2 = std::move(ptr1); ptr2 = std::move(ptr1);
@ -67,8 +67,8 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Allocating (2) new unique_ptr<int[10]>..." << endl; log.info() << "Allocating (2) new unique_ptr<int[10]>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(10); Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(10);
Memory::unique_ptr<int> ptr2 = Memory::make_unique<int>(10); Memory::UniquePtr<int> ptr2 = Memory::make_unique<int>(10);
log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl; log.info() << "Moving ptr1 => ptr2 (ptr1 should be freed)..." << endl;
ptr2 = std::move(ptr1); 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 // NOTE: This wasn't working because of a missing operator[] delete in the allocator
log.info() << "Allocating unique_ptr<int>*..." << endl; log.info() << "Allocating unique_ptr<int>*..." << endl;
Memory::unique_ptr<int> *ptrptr = new Memory::unique_ptr<int>[10]; Memory::UniquePtr<int> *ptrptr = new Memory::UniquePtr<int>[10];
delete[] ptrptr; delete[] ptrptr;
log.info() << "Should be deleted by now..." << endl; log.info() << "Should be deleted by now..." << endl;
@ -87,7 +87,7 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Stackallocating Array<bse::unique_ptr<int>, 10>..." << endl; log.info() << "Stackallocating Array<bse::unique_ptr<int>, 10>..." << endl;
Container::Array<Memory::unique_ptr<int>, 10> arr; Container::Array<Memory::UniquePtr<int>, 10> arr;
log.info() << "Populating slot 0..." << endl; log.info() << "Populating slot 0..." << endl;
arr[0] = Memory::make_unique<int>(1); arr[0] = Memory::make_unique<int>(1);
log.info() << "Moving slot 0 to slot 1..." << endl; log.info() << "Moving slot 0 to slot 1..." << endl;
@ -98,7 +98,7 @@ void SmartPointerDemo::run() {
{ {
log.info() << "Heapallocating Array<bse::unique_ptr<int>, 10>..." << endl; log.info() << "Heapallocating Array<bse::unique_ptr<int>, 10>..." << endl;
Container::Array<Memory::unique_ptr<int>, 10> *arr = new Container::Array<Memory::unique_ptr<int>, 10>; Container::Array<Memory::UniquePtr<int>, 10> *arr = new Container::Array<Memory::UniquePtr<int>, 10>;
log.info() << "Populating slot 0..." << endl; log.info() << "Populating slot 0..." << endl;
(*arr)[0] = Memory::make_unique<int>(1); (*arr)[0] = Memory::make_unique<int>(1);
log.info() << "Moving slot 0 to slot 1..." << endl; log.info() << "Moving slot 0 to slot 1..." << endl;
@ -111,7 +111,7 @@ void SmartPointerDemo::run() {
{ {
log.info() << "ArrayList<bse::unique_ptr<int>>..." << endl; log.info() << "ArrayList<bse::unique_ptr<int>>..." << endl;
Container::Vector<Memory::unique_ptr<int>> vec; Container::Vector<Memory::UniquePtr<int>> vec;
log.info() << "2x insertion" << endl; log.info() << "2x insertion" << endl;
vec.push_back(Memory::make_unique<int>(1)); vec.push_back(Memory::make_unique<int>(1));
vec.push_back(Memory::make_unique<int>(2)); vec.push_back(Memory::make_unique<int>(2));

View File

@ -2,24 +2,23 @@
#define MYSTDLIB_INCLUDE_H_ #define MYSTDLIB_INCLUDE_H_
#include <utility> #include <utility>
#include <cstdint>
namespace Memory { namespace Memory {
// add using byte or sth to replace char template<typename T>
void memcpy(T *destination, const T *source, std::size_t count = 1) {
template<typename T> for (unsigned int i = 0; i < count; ++i) {
void memcpy(T* destination, const T* source, std::size_t count = 1) { *(destination + i) = *(source + i);
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<typename T> template<typename T>
void zero(T* destination) { void zero(T *destination) {
memset(reinterpret_cast<char*>(destination), '\0', sizeof(T)); memset(reinterpret_cast<uint8_t *>(destination), '\0', sizeof(T));
} }
} // namespace bse } // namespace bse

View File

@ -2,6 +2,7 @@
#define UniquePointer_Include_H_ #define UniquePointer_Include_H_
#include <utility> #include <utility>
#include "lib/util/RestrictedConstructors.h"
// https://en.cppreference.com/w/cpp/memory/unique_ptr // 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 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 // T_ is the bare type without extents (int in both cases), so we have a
// int* pointer type for both unique_ptr<int> and unique_ptr<int[]> // int* pointer type for both unique_ptr<int> and unique_ptr<int[]>
/**
* This class implements a simple managed pointer with a single owner.
*
* @tparam T The type of object the pointer holds
*/
template<typename T> template<typename T>
class unique_ptr { class UniquePtr {
private:
using T_ = std::remove_extent_t<T>; using T_ = std::remove_extent_t<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<T>) {
delete[] ptr;
} else {
delete ptr;
}
ptr = nullptr;
}
public: public:
// Forbid copying
unique_ptr(const unique_ptr &copy) = delete;
unique_ptr &operator=(const unique_ptr &copy) = delete;
// Construction // Construction
unique_ptr() = default; // Allow declaration without explicit definition UniquePtr() = default; // Allow declaration without explicit definition
template<typename t, typename... Args> MakeUncopyable(UniquePtr)
friend typename std::enable_if_t<!std::is_array_v<t>, unique_ptr<t>>
make_unique(Args &&... args);
template<typename t>
friend typename std::enable_if_t<std::is_array_v<t>, unique_ptr<t>>
make_unique(std::size_t size);
// Deletion
~unique_ptr() {
del();
}
// Moving // Moving
unique_ptr(unique_ptr &&move) noexcept { reset(move.release()); }; UniquePtr(UniquePtr &&move) noexcept { reset(move.release()); };
// Implicit upcasting is needed: for sth like // Implicit upcasting is needed: unique_ptr<Thread> ptr = make_unique<IdleThread>();
// unique_ptr<Thread> ptr = make_unique<IdleThread>(); // Not explicit for the same reason
// IdleThread is derived from Thread so the assert passes
template<typename t> template<typename t>
unique_ptr(unique_ptr<t> &&move) noexcept { UniquePtr(UniquePtr<t> &&move) noexcept { // NOLINT(google-explicit-constructor)
static_assert(std::is_base_of_v<T, t>, "Has to be derived type"); static_assert(std::is_base_of_v<T, t>, "Has to be derived type");
reset(move.release()); reset(move.release());
} }
unique_ptr &operator=(unique_ptr &&move) noexcept { UniquePtr &operator=(UniquePtr &&move) noexcept {
reset(move.release()); reset(move.release());
return *this; return *this;
} }
// Resetting: Replaces managed object, deleting the old one ~UniquePtr() {
del();
}
// Utility construction
template<typename t, typename... Args>
friend typename std::enable_if_t<!std::is_array_v<t>, UniquePtr<t>>
make_unique(Args &&... args);
template<typename t>
friend typename std::enable_if_t<std::is_array_v<t>, UniquePtr<t>>
make_unique(std::size_t size);
/**
* Delete the contained object and reset the pointer.
*/
void reset() { del(); } 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) { void reset(T_ *pt) {
del(); del();
ptr = pt; ptr = pt;
} }
// Release: Releases ownership without deletion /**
* Release the contained object without deleting it.
*
* @return The contained pointer
*/
T_ *release() { T_ *release() {
// T* old = ptr; // T* old = ptr;
// ptr = nullptr; // ptr = nullptr;
@ -90,7 +87,11 @@ public:
return std::exchange(ptr, nullptr); 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 { T_ *get() const {
return ptr; return ptr;
} }
@ -108,12 +109,31 @@ public:
explicit operator bool() const { return (ptr != nullptr); } 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 // These are only for array unique_ptr but I didn't enforce that
T_ &operator[](std::size_t i) { return ptr[i]; } T_ &operator[](std::size_t i) { return ptr[i]; }
const T_ &operator[](std::size_t i) const { 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<IdleThread> 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<T>) {
delete[] ptr;
} else {
delete ptr;
}
ptr = nullptr;
}
}; };
// make_unique implementation ======================================= // make_unique implementation =======================================
@ -123,16 +143,16 @@ public:
template<typename T, typename... Args> template<typename T, typename... Args>
// We make the return type dependent on whether T is an array type or not // We make the return type dependent on whether T is an array type or not
typename std::enable_if_t<!std::is_array_v<T>, unique_ptr<T>> typename std::enable_if_t<!std::is_array_v<T>, UniquePtr<T>>
make_unique(Args &&... args) { make_unique(Args &&... args) {
return unique_ptr<T>(new T(std::forward<Args>(args)...)); return UniquePtr<T>(new T(std::forward<Args>(args)...));
} }
template<typename T> template<typename T>
typename std::enable_if_t<std::is_array_v<T>, unique_ptr<T>> typename std::enable_if_t<std::is_array_v<T>, UniquePtr<T>>
make_unique(std::size_t size) { make_unique(std::size_t size) {
using T_ = typename std::remove_extent_t<T>; using T_ = typename std::remove_extent_t<T>;
return unique_ptr<T>(new T_[size]); return UniquePtr<T>(new T_[size]);
} }
} // namespace bse } // namespace bse