From d4ac6b63a731db2eb63db5447e22c07a2097dda6 Mon Sep 17 00:00:00 2001 From: "Alan Manuel K. Gloria" Date: Tue, 5 Aug 2008 21:48:44 +0800 Subject: [PATCH] heaps, types: added LIFO-style memory allocation, primarily for continuations When using LIFO-style memory allocation, objects may be manually deallocated in the reverse order of allocation. Object deallocation may silently fail (no error conditions) if objects are deallocated in the wrong order. However, objects allocated on the LIFO-style memory are still subject to garbage collection; if they survive the collection, they are no longer in the LIFO space (and deallocation will silently fail), but they will be considered as part of the "main" heap. --- inc/heaps.hpp | 65 +++++++++++++++++++++++++++++++++++-- src/heaps.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++---- src/types.cpp | 11 +++---- 3 files changed, 148 insertions(+), 16 deletions(-) diff --git a/inc/heaps.hpp b/inc/heaps.hpp index 501d854..87fa517 100644 --- a/inc/heaps.hpp +++ b/inc/heaps.hpp @@ -35,9 +35,37 @@ class Semispace{ friend class Heap; }; +/*last-in-first-out allocation semispace*/ +class LifoSemispace { +private: + void* mem; + void* allocpt; + void* end; + size_t prevalloc; +public: + LifoSemispace(size_t sz); + ~LifoSemispace(); + void* alloc(size_t sz); + void dealloc(void*); // used only in a constructor-fail delete + void normal_dealloc(Generic*); + bool can_fit(size_t) const; + size_t size(void) const { + return (size_t) (((char*) end) - ((char*) mem)); + } + size_t used(void) const { + return (size_t) (((char*) end) - ((char*) allocpt)); + } + + /*no need to clone LIFO semispaces: they never get passed around*/ + friend class Heap; +}; + +class LifoHeap; + class Heap{ private: boost::scoped_ptr s; + boost::scoped_ptr ls; bool tight; protected: /*conceptually these should be scoped_ptr's, but @@ -49,15 +77,42 @@ class Heap{ std::vector > other_spaces; virtual void get_root_set(std::stack&) =0; size_t get_total_heap_size(void) const; - void GC(size_t); + void GC(size_t,bool from_lifo=0); public: void* alloc(size_t); void dealloc(void*); boost::shared_ptr to_new_semispace(Generic*&) const; - Heap(void) : s(new Semispace(64)), tight(0) {}; + Heap(void); virtual ~Heap(){}; + + void* lifo_alloc(size_t); + void lifo_dealloc(void*); + void lifo_normal_dealloc(Generic*); + + LifoHeap lifo(void); + }; +class LifoHeap { +private: + Heap* hp; +public: + LifoHeap(Heap* nhp) : hp(nhp) {} + + void* alloc(size_t sz){return hp->lifo_alloc(sz);} + void dealloc(void* pt){return hp->lifo_dealloc(pt);} + void normal_dealloc(Generic* gp){return hp->lifo_normal_dealloc(gp);} +}; + +inline Heap::Heap(void) + : s(new Semispace(64)), + tight(0), + ls(new LifoSemispace(64)) {} + +inline LifoHeap Heap::lifo(void){ + return LifoHeap(this); +} + inline void* operator new(size_t n, Semispace& sp){ return sp.alloc(n); } @@ -78,6 +133,12 @@ inline void operator delete(void* p, Heap& hp){ /*necessary for proper cleanup in case the ctor throws*/ hp.dealloc(p); } +inline void* operator new(size_t n, LifoHeap hp){ + return hp.alloc(n); +} +inline void operator delete(void* p, LifoHeap hp){ + hp.dealloc(p); +} #endif //HEAPS_H diff --git a/src/heaps.cpp b/src/heaps.cpp index d3a027f..d6ee069 100644 --- a/src/heaps.cpp +++ b/src/heaps.cpp @@ -4,7 +4,7 @@ #include /*----------------------------------------------------------------------------- -Semispaces +Semispace -----------------------------------------------------------------------------*/ static ptrdiff_t moveallreferences(void* mem, void* allocpt, void* nmem){ @@ -125,18 +125,72 @@ std::pair, Generic* > } /*----------------------------------------------------------------------------- -Heaps +LifoSemispace +-----------------------------------------------------------------------------*/ + +LifoSemispace::LifoSemispace(size_t sz) { + mem = 0; + try{ + mem = malloc(sz); + if(mem == NULL) throw std::bad_alloc(); + allocpt = end = ((char*) mem) + sz; + prevalloc = 0; + }catch(...){ + if(mem) free(mem); + throw; + } +} + +LifoSemispace::~LifoSemispace(){ + while(allocpt < end){ + normal_dealloc((Generic*) allocpt); + } + free(mem); +} + +void* LifoSemispace::alloc(size_t sz){ + /*assertion; shouldn't trigger*/ + if(!can_fit(sz)) throw std::bad_alloc(); + allocpt = ((char*)allocpt) - sz; + prevalloc = sz; + return allocpt; +} + +/*this should only get called on a failed construction*/ +void LifoSemispace::dealloc(void* pt){ + if(pt != allocpt) throw std::bad_alloc(); + allocpt = ((char*)allocpt) + prevalloc; + prevalloc = 0; +} + +/*this should get called after the object is fully constructed*/ +void LifoSemispace::normal_dealloc(Generic* pt){ + if(pt != allocpt) return; // do nothing if we can't trivially pop off + /*get the size of the object*/ + size_t sz = pt->get_size(); + /*destroy*/ + pt->~Generic(); + allocpt = ((char*)allocpt) + sz; +} + +bool LifoSemispace::can_fit(size_t sz) const { + return (((char*)allocpt) - sz) >= (char*)mem; +} + +/*----------------------------------------------------------------------------- +Heap -----------------------------------------------------------------------------*/ /*preconditions: other_spaces must be locked! */ size_t Heap::get_total_heap_size(void) const{ - size_t sz = s->size(); + size_t sz = s->used(); std::vector >::const_iterator i; for(i = other_spaces.begin(); i != other_spaces.end(); ++i){ - sz += (*i)->size(); + sz += (*i)->used(); } + sz += ls->used(); return sz; } @@ -161,13 +215,18 @@ static void copy_set(std::stack& tocopy, } /*explicit recursion copy*/ -void Heap::GC(size_t insurance){ +void Heap::GC(size_t insurance, bool from_lifo){ /*insert mutex locking of other_spaces here*/ - size_t sz = get_total_heap_size() + insurance; + /*calculate sizes*/ + size_t sz = get_total_heap_size(); + size_t lsz = ls->size(); + if(from_lifo) lsz += insurance * 2; + else sz += insurance; if(tight){ sz = sz * 2;} boost::scoped_ptr ns(new Semispace(sz)); + boost::scoped_ptr nls(new LifoSemispace(lsz)); ToPointerLock toptrs; std::stack tocopy; @@ -175,13 +234,15 @@ void Heap::GC(size_t insurance){ copy_set(tocopy, toptrs, *ns); s.swap(ns); //shouldn't throw + ls.swap(nls); /*successful GC, don't bother clearing*/ toptrs.good(); other_spaces.clear(); ns.reset(); /*determine if resizing necessary*/ - size_t used = s->used() + insurance; + size_t used = s->used(); + if(!from_lifo) used += insurance; tight = 0; if(used <= sz / 4){ s->resize(sz / 2); @@ -203,6 +264,19 @@ void Heap::dealloc(void* check){ s->dealloc(check); } +void* Heap::lifo_alloc(size_t sz){ + if(!ls->can_fit(sz)){ + GC(sz, 1); + } + return ls->alloc(sz); +} +void Heap::lifo_dealloc(void* check){ + ls->dealloc(check); +} +void Heap::lifo_normal_dealloc(Generic* gp){ + ls->normal_dealloc(gp); +} + boost::shared_ptr Heap::to_new_semispace(Generic*& gp) const { /*Determine the size*/ ToPointerLock toptrs; diff --git a/src/types.cpp b/src/types.cpp index 9fc392d..33f3c79 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -231,9 +231,9 @@ class ClosureVector : public C { /*constructs a Closure of the given size with the given Executor The constructed Closure may be any of the above implementations. */ -template +template static C* NewClosureImpl( - Heap& hp, boost::shared_ptr c, size_t sz) { + A hp, boost::shared_ptr c, size_t sz) { switch(sz){ /*Use statically-determined sizes as much as possible*/ case 0: return new(hp) EmptyClosure(c); @@ -253,16 +253,13 @@ Closure* NewClosure(Heap& hp, Executor* c, size_t sz) { return NewClosure(hp, boost::shared_ptr(c), sz); } Closure* NewClosure(Heap& hp, boost::shared_ptr c, size_t sz) { - return NewClosureImpl(hp, c, sz); + return NewClosureImpl(hp, c, sz); } -/*TODO: make these allocate in the LIFO allocation structure, when that -is implemented -*/ KClosure* NewKClosure(Heap& hp, Executor* c, size_t sz) { return NewKClosure(hp, boost::shared_ptr(c), sz); } KClosure* NewKClosure(Heap& hp, boost::shared_ptr c, size_t sz) { - return NewClosureImpl(hp, c, sz); + return NewClosureImpl(hp.lifo(), c, sz); } /*DEBUG CODE*/