Cleanup memory classes
This commit is contained in:
@ -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();
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 ©) = 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<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
|
||||
|
Reference in New Issue
Block a user