diff --git a/src/GC/include/chunk.hpp b/src/GC/include/chunk.hpp index 1e63869..595b50b 100644 --- a/src/GC/include/chunk.hpp +++ b/src/GC/include/chunk.hpp @@ -18,7 +18,7 @@ namespace GC uintptr_t *const m_start {nullptr}; const size_t m_size {0}; - Chunk(size_t size, uintptr_t *start) : m_size(size), m_start(start) {} + Chunk(size_t size, uintptr_t *start) : m_start(start), m_size(size) {} Chunk(const Chunk *const c) : m_marked(c->m_marked), m_start(c->m_start), m_size(c->m_size) {} Chunk(const Chunk &c) : m_marked(c.m_marked), m_start(c.m_start), m_size(c.m_size) {} }; diff --git a/src/GC/include/heap.hpp b/src/GC/include/heap.hpp index 0cc7f73..be1d199 100644 --- a/src/GC/include/heap.hpp +++ b/src/GC/include/heap.hpp @@ -11,7 +11,7 @@ #define HEAP_SIZE 65536 #define FREE_THRESH (uint)20 -// #define DEBUG +#define DEBUG namespace GC { @@ -45,67 +45,22 @@ namespace GC std::free((char *)m_heap); } - /** - * If m_instance is a nullptr (the singleton has not - * been initialized yet) initialize the singleton - * and return the pointer. Otherwise return the - * previously initialized pointer. - * - * @returns The pointer to the heap singleton. - */ - static Heap *the() - { - if (m_instance) // if m_instance is not a nullptr - return m_instance; - m_instance = new Heap(); - return m_instance; - } - - /** - * Advances an iterator and returns an element - * at position `n`. - * - * @param list The list to retrieve an element from. - * - * @param n The position to retrieve an element at. - * - * @returns The pointer to the chunk at position n in list. - */ - static Chunk *get_at(std::vector &list, size_t n) - { - auto iter = list.begin(); - if (!n) - return *iter; - std::advance(iter, n); - return *iter; - } - - /** - * Returns a bool whether the profiler is enabled - * or not. - * - * @returns True or false if the profiler is enabled - * or disabled respectively. - */ - inline bool profiler_enabled() { - auto heap = Heap::the(); - return heap->m_profiler_enable; - } - char *const m_heap; size_t m_size {0}; - inline static Heap *m_instance {nullptr}; + // static Heap *m_instance {nullptr}; uintptr_t *m_stack_top {nullptr}; bool m_profiler_enable {false}; std::vector m_allocated_chunks; std::vector m_freed_chunks; + static bool profiler_enabled(); + static Chunk *get_at(std::vector &list, size_t n); void collect(); - void sweep(Heap *heap); + void sweep(Heap &heap); Chunk *try_recycle_chunks(size_t size); - void free(Heap *heap); - void free_overlap(Heap *heap); + void free(Heap &heap); + void free_overlap(Heap &heap); void mark(uintptr_t *start, const uintptr_t *end, std::vector &worklist); void print_line(Chunk *chunk); void print_worklist(std::vector &list); @@ -113,26 +68,36 @@ namespace GC public: /** - * These are the only two functions which are exposed + * These are the only five functions which are exposed * as the API for LLVM. At the absolute start of the * program the developer has to call init() to ensure * that the address of the topmost stack frame is * saved as the limit for scanning the stack in collect. */ + /** + * This implementation of the() guarantees laziness + * on the instance and a correct destruction with + * the destructor. + * + * @returns The singleton object. + */ + static Heap& the() + { + static Heap instance; + return instance; + } + static void init(); static void dispose(); static void *alloc(size_t size); void set_profiler(bool mode); + // Stop the compiler from generating copy-methods + Heap(Heap const&) = delete; + Heap& operator=(Heap const&) = delete; + #ifdef DEBUG - static Heap *debug_the() - { - if (m_instance) // if m_instance is not a nullptr - return m_instance; - m_instance = new Heap(); - return m_instance; - } void collect(CollectOption flags); // conditional collection void check_init(); // print dummy things void print_contents(); // print dummy things diff --git a/src/GC/lib/heap.cpp b/src/GC/lib/heap.cpp index ff0a667..f7aa92b 100644 --- a/src/GC/lib/heap.cpp +++ b/src/GC/lib/heap.cpp @@ -14,6 +14,8 @@ using std::cout, std::endl, std::vector, std::hex, std::dec; namespace GC { + + /** * Initialises the heap singleton and saves the address * of the calling function's stack frame as the stack_top. @@ -22,12 +24,12 @@ namespace GC */ void Heap::init() { - Heap *heap = Heap::the(); - if (heap->profiler_enabled()) + Heap &heap = Heap::the(); + if (heap.profiler_enabled()) Profiler::record(HeapInit); -// clang complains because arg for __b_f_a is not 0 which is unsafe +// clang complains because arg for __b_f_a is not 0 which is "unsafe" #pragma clang diagnostic ignored "-Wframe-address" - heap->m_stack_top = static_cast(__builtin_frame_address(1)); + heap.m_stack_top = static_cast(__builtin_frame_address(1)); } /** @@ -37,10 +39,9 @@ namespace GC */ void Heap::dispose() { - Heap *heap = Heap::the(); - if (heap->profiler_enabled()) + Heap &heap = Heap::the(); + if (heap.profiler_enabled()) Profiler::dispose(); - delete heap; } /** @@ -55,8 +56,8 @@ namespace GC void *Heap::alloc(size_t size) { // Singleton - Heap *heap = Heap::the(); - bool profiler_enabled = heap->profiler_enabled(); + Heap &heap = Heap::the(); + bool profiler_enabled = heap.profiler_enabled(); if (profiler_enabled) Profiler::record(AllocStart, size); @@ -67,15 +68,15 @@ namespace GC return nullptr; } - if (heap->m_size + size > HEAP_SIZE) + if (heap.m_size + size > HEAP_SIZE) { - heap->collect(); + heap.collect(); // If memory is not enough after collect, crash with OOM error throw std::runtime_error(std::string("Error: Heap out of memory")); } // If a chunk was recycled, return the old chunk address - Chunk *reused_chunk = heap->try_recycle_chunks(size); + Chunk *reused_chunk = heap.try_recycle_chunks(size); if (reused_chunk != nullptr) { if (profiler_enabled) @@ -85,15 +86,14 @@ namespace GC // If no free chunks was found (reused_chunk is a nullptr), // then create a new chunk - auto new_chunk = new Chunk(size, (uintptr_t *)(heap->m_heap + heap->m_size)); + auto new_chunk = new Chunk(size, (uintptr_t *)(heap.m_heap + heap.m_size)); - heap->m_size += size; - heap->m_allocated_chunks.push_back(new_chunk); + heap.m_size += size; + heap.m_allocated_chunks.push_back(new_chunk); if (profiler_enabled) Profiler::record(NewChunk, new_chunk); - // new_chunk should probably be a unique pointer, if that isn't implicit already return new_chunk->m_start; } @@ -115,12 +115,12 @@ namespace GC */ Chunk *Heap::try_recycle_chunks(size_t size) { - auto heap = Heap::the(); + Heap &heap = Heap::the(); // Check if there are any freed chunks large enough for current request - for (size_t i = 0; i < heap->m_freed_chunks.size(); i++) + for (size_t i = 0; i < heap.m_freed_chunks.size(); i++) { - auto chunk = Heap::get_at(heap->m_freed_chunks, i); - auto iter = heap->m_freed_chunks.begin(); + auto chunk = Heap::get_at(heap.m_freed_chunks, i); + auto iter = heap.m_freed_chunks.begin(); advance(iter, i); if (chunk->m_size > size) { @@ -129,17 +129,17 @@ namespace GC size_t diff = chunk->m_size - size; auto chunk_complement = new Chunk(diff, chunk->m_start + chunk->m_size); - heap->m_freed_chunks.erase(iter); - heap->m_freed_chunks.push_back(chunk_complement); - heap->m_allocated_chunks.push_back(chunk); + heap.m_freed_chunks.erase(iter); + heap.m_freed_chunks.push_back(chunk_complement); + heap.m_allocated_chunks.push_back(chunk); return chunk; } else if (chunk->m_size == size) { // Reuse the whole chunk - heap->m_freed_chunks.erase(iter); - heap->m_allocated_chunks.push_back(chunk); + heap.m_freed_chunks.erase(iter); + heap.m_allocated_chunks.push_back(chunk); return chunk; } } @@ -147,6 +147,37 @@ namespace GC return nullptr; } + /** + * Advances an iterator and returns an element + * at position `n`. + * + * @param list The list to retrieve an element from. + * + * @param n The position to retrieve an element at. + * + * @returns The pointer to the chunk at position n in list. + */ + Chunk *Heap::get_at(std::vector &list, size_t n) + { + auto iter = list.begin(); + if (!n) + return *iter; + std::advance(iter, n); + return *iter; + } + + /** + * Returns a bool whether the profiler is enabled + * or not. + * + * @returns True or false if the profiler is enabled + * or disabled respectively. + */ + bool Heap::profiler_enabled() { + Heap &heap = Heap::the(); + return heap.m_profiler_enable; + } + /** * Collection phase of the garbage collector. When * an allocation is requested and there is no space @@ -156,20 +187,20 @@ namespace GC */ void Heap::collect() { - auto heap = Heap::the(); + Heap &heap = Heap::the(); - if (heap->profiler_enabled()) + if (heap.profiler_enabled()) Profiler::record(CollectStart); // get current stack frame auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); - if (heap->m_stack_top == nullptr) + if (heap.m_stack_top == nullptr) throw std::runtime_error(std::string("Error: Heap is not initialized, read the docs!")); - uintptr_t *stack_top = heap->m_stack_top; + uintptr_t *stack_top = heap.m_stack_top; - auto work_list = heap->m_allocated_chunks; + auto work_list = heap.m_allocated_chunks; mark(stack_bottom, stack_top, work_list); sweep(heap); @@ -195,8 +226,8 @@ namespace GC */ void Heap::mark(uintptr_t *start, const uintptr_t* const end, vector &worklist) { - Heap *heap = Heap::the(); - bool profiler_enabled = heap->profiler_enabled(); + Heap &heap = Heap::the(); + bool profiler_enabled = heap.m_profiler_enable; if (profiler_enabled) Profiler::record(MarkStart); @@ -249,12 +280,12 @@ namespace GC * * @param heap Pointer to the heap singleton instance. */ - void Heap::sweep(Heap *heap) + void Heap::sweep(Heap &heap) { - auto iter = heap->m_allocated_chunks.begin(); - bool profiler_enabled = heap->profiler_enabled(); + auto iter = heap.m_allocated_chunks.begin(); + bool profiler_enabled = heap.m_profiler_enable; // This cannot "iter != stop", results in seg fault, since the end gets updated, I think. - while (iter != heap->m_allocated_chunks.end()) + while (iter != heap.m_allocated_chunks.end()) { Chunk *chunk = *iter; @@ -270,8 +301,8 @@ namespace GC // the list of allocated chunks if (profiler_enabled) Profiler::record(ChunkSwept, chunk); - heap->m_freed_chunks.push_back(chunk); - iter = heap->m_allocated_chunks.erase(iter); + heap.m_freed_chunks.push_back(chunk); + iter = heap.m_allocated_chunks.erase(iter); } } } @@ -289,22 +320,22 @@ namespace GC * @param heap Heap singleton instance, only for avoiding * redundant calls to the singleton get */ - void Heap::free(Heap *heap) + void Heap::free(Heap &heap) { - if (heap->m_freed_chunks.size() > FREE_THRESH) + if (heap.m_freed_chunks.size() > FREE_THRESH) { - bool profiler_enabled = heap->profiler_enabled(); - while (heap->m_freed_chunks.size()) + bool profiler_enabled = heap.profiler_enabled(); + while (heap.m_freed_chunks.size()) { - auto chunk = heap->m_freed_chunks.back(); - heap->m_freed_chunks.pop_back(); + auto chunk = heap.m_freed_chunks.back(); + heap.m_freed_chunks.pop_back(); if (profiler_enabled) Profiler::record(ChunkFreed, chunk); delete chunk; } } // if there are chunks but not more than FREE_THRESH - else if (heap->m_freed_chunks.size()) + else if (heap.m_freed_chunks.size()) { // essentially, always check for overlap between // chunks before finishing the allocation @@ -327,18 +358,18 @@ namespace GC * larger chunks. Should remove get_at() to indexing, * since that's constant. */ - void Heap::free_overlap(Heap *heap) // borde göra en record(ChunkFreed) på onödiga chunks + void Heap::free_overlap(Heap &heap) // borde göra en record(ChunkFreed) på onödiga chunks { std::vector filtered; size_t i = 0; - auto prev = Heap::get_at(heap->m_freed_chunks, i++); + auto prev = Heap::get_at(heap.m_freed_chunks, i++); prev->m_marked = true; filtered.push_back(prev); cout << filtered.back()->m_start << endl; - for (; i < heap->m_freed_chunks.size(); i++) + for (; i < heap.m_freed_chunks.size(); i++) { prev = filtered.back(); - auto next = Heap::get_at(heap->m_freed_chunks, i); + auto next = Heap::get_at(heap.m_freed_chunks, i); auto p_start = (uintptr_t)(prev->m_start); auto p_size = (uintptr_t)(prev->m_size); auto n_start = (uintptr_t)(next->m_start); @@ -348,9 +379,9 @@ namespace GC filtered.push_back(next); } } - heap->m_freed_chunks.swap(filtered); + heap.m_freed_chunks.swap(filtered); - bool profiler_enabled = heap->profiler_enabled(); + bool profiler_enabled = heap.m_profiler_enable; // After swap m_freed_chunks contains still available chunks // and filtered contains all the chunks, so delete unused chunks for (Chunk *chunk : filtered) @@ -376,9 +407,9 @@ namespace GC */ void Heap::check_init() { - auto heap = Heap::the(); - cout << "Heap addr:\t" << heap << "\n"; - cout << "GC m_stack_top:\t" << heap->m_stack_top << "\n"; + Heap &heap = Heap::the(); + cout << "Heap addr:\t" << &heap << "\n"; + cout << "GC m_stack_top:\t" << heap.m_stack_top << "\n"; auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); cout << "GC stack_bottom:\t" << stack_bottom << endl; } @@ -392,9 +423,9 @@ namespace GC { set_profiler(true); - auto heap = Heap::the(); + Heap &heap = Heap::the(); - if (heap->profiler_enabled()) + if (heap.m_profiler_enable) Profiler::record(CollectStart); cout << "DEBUG COLLECT\nFLAGS: "; @@ -409,10 +440,10 @@ namespace GC // get the frame adress, whwere local variables and saved registers are located auto stack_bottom = reinterpret_cast(__builtin_frame_address(0)); cout << "Stack bottom in collect:\t" << stack_bottom << "\n"; - uintptr_t *stack_top = heap->m_stack_top; + uintptr_t *stack_top = heap.m_stack_top; cout << "Stack end in collect:\t " << stack_top << endl; - auto work_list = heap->m_allocated_chunks; + auto work_list = heap.m_allocated_chunks; if (flags & MARK) mark(stack_bottom, stack_top, work_list); @@ -476,21 +507,21 @@ namespace GC void Heap::print_contents() { - auto heap = Heap::the(); - if (heap->m_allocated_chunks.size()) + Heap &heap = Heap::the(); + if (heap.m_allocated_chunks.size()) { - cout << "\nALLOCATED CHUNKS #" << dec << heap->m_allocated_chunks.size() << endl; - for (auto chunk : heap->m_allocated_chunks) + cout << "\nALLOCATED CHUNKS #" << dec << heap.m_allocated_chunks.size() << endl; + for (auto chunk : heap.m_allocated_chunks) print_line(chunk); } else { cout << "NO ALLOCATIONS\n" << endl; } - if (heap->m_freed_chunks.size()) + if (heap.m_freed_chunks.size()) { - cout << "\nFREED CHUNKS #" << dec << heap->m_freed_chunks.size() << endl; - for (auto fchunk : heap->m_freed_chunks) + cout << "\nFREED CHUNKS #" << dec << heap.m_freed_chunks.size() << endl; + for (auto fchunk : heap.m_freed_chunks) print_line(fchunk); } else @@ -501,8 +532,8 @@ namespace GC void Heap::set_profiler(bool mode) { - auto heap = Heap::the(); - heap->m_profiler_enable = mode; + Heap &heap = Heap::the(); + heap.m_profiler_enable = mode; } void Heap::print_allocated_chunks(Heap *heap) { diff --git a/src/GC/tests/extern_lib.cpp b/src/GC/tests/extern_lib.cpp index ef99130..fa30051 100644 --- a/src/GC/tests/extern_lib.cpp +++ b/src/GC/tests/extern_lib.cpp @@ -3,18 +3,18 @@ #include "heap.hpp" -GC::Heap *singleton_test(); -void init_gc(GC::Heap *heap); -void frame_test(GC::Heap *heap); +GC::Heap& singleton_test(); +void init_gc(GC::Heap& heap); +void frame_test(GC::Heap& heap); int main() { std::cout << "in main" << std::endl; - auto heap = singleton_test(); + GC::Heap &heap = singleton_test(); init_gc(heap); frame_test(heap); - heap->dispose(); + heap.dispose(); return 0; } @@ -28,12 +28,12 @@ int main() { * * @return Pointer to the Heap singleton instance */ -GC::Heap *singleton_test() { +GC::Heap& singleton_test() { std::cout << "TESTING SINGLETON INSTANCES" << std::endl; std::cout << "===========================" << std::endl; - std::cout << "Call 1:\t" << GC::Heap::debug_the() << std::endl; // First call which initializes the singleton instance - GC::Heap *heap = GC::Heap::debug_the(); // Second call which should return the initialized instance - std::cout << "Call 2:\t" << heap << std::endl; + std::cout << "Call 1:\t" << &GC::Heap::the() << std::endl; // First call which initializes the singleton instance + GC::Heap &heap = GC::Heap::the(); // Second call which should return the initialized instance + std::cout << "Call 2:\t" << &heap << std::endl; std::cout << "===========================" << std::endl; return heap; } @@ -50,11 +50,11 @@ GC::Heap *singleton_test() { * @param heap The Heap pointer to the singleton instance. * */ -void init_gc(GC::Heap *heap){ +void init_gc(GC::Heap& heap){ std::cout << "\n\n INITIALIZING THE HEAP" << std::endl; std::cout << "===========================" << std::endl; - heap->init(); - heap->set_profiler(true); + heap.init(); + heap.set_profiler(true); std::cout << "===========================" << std::endl; } @@ -76,7 +76,7 @@ void init_gc(GC::Heap *heap){ * * @param heap The Heap instance */ -void frame_test(GC::Heap *heap) { +void frame_test(GC::Heap& heap) { std::cout << "\n\n TESTING FRAME ADDRESSES" << std::endl; std::cout << "===========================" << std::endl; @@ -87,7 +87,7 @@ void frame_test(GC::Heap *heap) { auto prev_frame = reinterpret_cast(__builtin_frame_address(1)); // addr of prev stack frame std::cout << "Previous stack frame:\t" << prev_frame << std::endl; - heap->check_init(); // prints the saved absolute top of the stack + heap.check_init(); // prints the saved absolute top of the stack // auto alloced = heap->alloc(sizeof(unsigned long)); std::cout << "===========================" << std::endl; diff --git a/src/GC/todo.md b/src/GC/todo.md index dae634a..83fcf2c 100644 --- a/src/GC/todo.md +++ b/src/GC/todo.md @@ -4,9 +4,6 @@ Deliver to samuel ## GC TODO: -- Dokumentera - - Dokumentera alla filer och funktioner - - Skriva reference guide för Samuel - PR till master ## Tests TODO