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
}
void Scheduler::ready(Memory::unique_ptr<Thread> &&thread) {
void Scheduler::ready(Memory::UniquePtr<Thread> &&thread) {
Device::CPU::disable_int();
ready_queue.push_back(std::move(thread));
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;
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<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,
// 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;
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<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) {
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<Thread> *ptr) {
void Scheduler::kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr) {
Device::CPU::disable_int();
uint16_t prev_tid = (*active)->tid;
@ -128,7 +128,7 @@ void Scheduler::kill(uint16_t tid, Memory::unique_ptr<Thread> *ptr) {
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) {
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<Thread> *ptr) {
void Scheduler::nice_kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr) {
Device::CPU::disable_int();
for (Memory::unique_ptr<Thread> &thread: block_queue) {
for (Memory::UniquePtr<Thread> &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<Thread> *ptr) {
}
}
for (Memory::unique_ptr<Thread> &thread: ready_queue) {
for (Memory::UniquePtr<Thread> &thread: ready_queue) {
if (thread->tid == tid) {
thread->suicide();
Device::CPU::enable_int();

View File

@ -32,7 +32,7 @@ private:
*
* @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.
@ -40,7 +40,7 @@ private:
*
* @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.
@ -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<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.
@ -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<Thread> *ptr);
void kill(uint16_t tid, Memory::UniquePtr<Thread> *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<Thread> *ptr);
void nice_kill(uint16_t tid, Memory::UniquePtr<Thread> *ptr);
void nice_kill(uint16_t tid);
private:
Container::Vector<Memory::unique_ptr<Thread>> ready_queue;
Container::Vector<Memory::unique_ptr<Thread>> block_queue;
Container::Vector<Memory::unique_ptr<Thread>> exit_queue; // TODO: Manage exited threads
Container::Vector<Memory::UniquePtr<Thread>> ready_queue;
Container::Vector<Memory::UniquePtr<Thread>> block_queue;
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
// 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<Memory::unique_ptr<Thread>>::iterator active = nullptr;
Container::Vector<Memory::UniquePtr<Thread>>::iterator active = nullptr;
// TODO: Synchronization
};

View File

@ -34,7 +34,7 @@ public:
*/
template<typename T, typename... 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;
scheduler.ready(std::move(thread));

View File

@ -12,15 +12,15 @@ void SmartPointerDemo::run() {
{
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() << "Should be deleted by now..." << endl;
{
log.info() << "Allocating new unique_ptr<int>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(1);
Memory::unique_ptr<int> ptr2;
Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(1);
Memory::UniquePtr<int> ptr2;
log.info() << "*ptr1 == " << *ptr1 << ", (bool)ptr2 == " << static_cast<bool>(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<int>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(1);
Memory::unique_ptr<int> ptr2 = Memory::make_unique<int>(1);
Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(1);
Memory::UniquePtr<int> ptr2 = Memory::make_unique<int>(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<int[]>..." << endl;
Memory::unique_ptr<int[]> ptr = Memory::make_unique<int[]>(10);
Memory::UniquePtr<int[]> ptr = Memory::make_unique<int[]>(10);
ptr[0] = 1;
log.info() << "ptr[0] == " << ptr[0] << endl;
}
@ -55,8 +55,8 @@ void SmartPointerDemo::run() {
{
log.info() << "Allocating new unique_ptr<int[10]>..." << endl;
Memory::unique_ptr<int[]> ptr1 = Memory::make_unique<int[]>(10);
Memory::unique_ptr<int[]> ptr2;
Memory::UniquePtr<int[]> ptr1 = Memory::make_unique<int[]>(10);
Memory::UniquePtr<int[]> 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<int[10]>..." << endl;
Memory::unique_ptr<int> ptr1 = Memory::make_unique<int>(10);
Memory::unique_ptr<int> ptr2 = Memory::make_unique<int>(10);
Memory::UniquePtr<int> ptr1 = Memory::make_unique<int>(10);
Memory::UniquePtr<int> ptr2 = Memory::make_unique<int>(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<int>*..." << endl;
Memory::unique_ptr<int> *ptrptr = new Memory::unique_ptr<int>[10];
Memory::UniquePtr<int> *ptrptr = new Memory::UniquePtr<int>[10];
delete[] ptrptr;
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;
Container::Array<Memory::unique_ptr<int>, 10> arr;
Container::Array<Memory::UniquePtr<int>, 10> arr;
log.info() << "Populating slot 0..." << endl;
arr[0] = Memory::make_unique<int>(1);
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;
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;
(*arr)[0] = Memory::make_unique<int>(1);
log.info() << "Moving slot 0 to slot 1..." << endl;
@ -111,7 +111,7 @@ void SmartPointerDemo::run() {
{
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;
vec.push_back(Memory::make_unique<int>(1));
vec.push_back(Memory::make_unique<int>(2));

View File

@ -2,24 +2,23 @@
#define MYSTDLIB_INCLUDE_H_
#include <utility>
#include <cstdint>
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) {
for (unsigned int i = 0; i < count; ++i) {
*(destination + i) = *(source + i);
}
template<typename T>
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<typename T>
void zero(T* destination) {
memset(reinterpret_cast<char*>(destination), '\0', sizeof(T));
}
template<typename T>
void zero(T *destination) {
memset(reinterpret_cast<uint8_t *>(destination), '\0', sizeof(T));
}
} // namespace bse

View File

@ -2,6 +2,7 @@
#define UniquePointer_Include_H_
#include <utility>
#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<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>
class unique_ptr {
private:
class UniquePtr {
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:
// Forbid copying
unique_ptr(const unique_ptr &copy) = delete;
unique_ptr &operator=(const unique_ptr &copy) = delete;
// Construction
unique_ptr() = default; // Allow declaration without explicit definition
UniquePtr() = default; // Allow declaration without explicit definition
template<typename t, typename... Args>
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();
}
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<Thread> ptr = make_unique<IdleThread>();
// IdleThread is derived from Thread so the assert passes
// Implicit upcasting is needed: unique_ptr<Thread> ptr = make_unique<IdleThread>();
// Not explicit for the same reason
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");
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<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(); }
/**
* 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<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 =======================================
@ -123,16 +143,16 @@ public:
template<typename T, typename... Args>
// 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) {
return unique_ptr<T>(new T(std::forward<Args>(args)...));
return UniquePtr<T>(new T(std::forward<Args>(args)...));
}
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) {
using T_ = typename std::remove_extent_t<T>;
return unique_ptr<T>(new T_[size]);
return UniquePtr<T>(new T_[size]);
}
} // namespace bse