Skip to content

Instantly share code, notes, and snippets.

@jhaberstro
Created November 5, 2010 22:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jhaberstro/664938 to your computer and use it in GitHub Desktop.
Save jhaberstro/664938 to your computer and use it in GitHub Desktop.
ScopeStack implementation stolen from Andreas Fredriksson's DICE presentation on Scope Stack Allocation.
// Simplified scope stack implementation
#include <new>
#include <cstdio>
#include <cassert>
typedef unsigned char u8;
class LinearAllocator {
public:
static const size_t Alignment = 16;
LinearAllocator(void *ptr_, size_t size)
: m_ptr((u8*)ptr_)
, m_end(m_ptr + size)
{}
static unsigned int alignedSize(size_t size) {
return (size + (Alignment - 1)) & ~(Alignment - 1);
}
void* allocate(size_t size) {
size = alignedSize(size);
u8 *result = m_ptr += size;
assert(result + size <= m_end);
return result;
}
void *ptr() { return m_ptr; }
void rewind(void *ptr) {
m_ptr = (u8*) ptr;
}
private:
u8 *m_ptr, *m_end;
};
struct Finalizer {
void (*fn)(void *ptr);
Finalizer *chain;
};
template <typename T>
void destructorCall(void *ptr) {
static_cast<T*>(ptr)->~T();
}
class ScopeStack {
private:
LinearAllocator& m_alloc;
void *m_rewindPoint;
Finalizer *m_finalizerChain;
static void* objectFromFinalizer(Finalizer *f) {
return ((u8*)f) + LinearAllocator::alignedSize(sizeof(Finalizer));
}
Finalizer *allocWithFinalizer(size_t size) {
return (Finalizer*) m_alloc.allocate(size + LinearAllocator::alignedSize(sizeof(Finalizer)));
}
public:
explicit ScopeStack(LinearAllocator& a)
: m_alloc(a)
, m_rewindPoint(a.ptr())
, m_finalizerChain(0)
{}
~ScopeStack() {
for (Finalizer *f = m_finalizerChain; f; f = f->chain) {
(*f->fn)(objectFromFinalizer(f));
}
m_alloc.rewind(m_rewindPoint);
}
template <typename T>
T* newObject() {
// Allocate memory for finalizer + object.
Finalizer* f = allocWithFinalizer(sizeof(T));
// Placement construct object in space after finalizer. Do this before
// linking in the finalizer for this object so nested calls will be
// finalized after this object.
T* result = new (objectFromFinalizer(f)) T;
// Link this finalizer onto the chain.
f->fn = &destructorCall<T>;
f->chain = m_finalizerChain;
m_finalizerChain = f;
return result;
}
template <typename T>
T* newPOD() {
return new (m_alloc.allocate(sizeof(T))) T;
}
// C++ suckage: need overloads ov newObject() for many arguments..
};
struct Foo {
static int count;
int num;
Foo() : num(count++) { printf("Foo ctor %d\n", num); }
~Foo() { printf("Foo dtor %d\n", num); }
};
int Foo::count;
u8 test_mem[65536];
int main()
{
LinearAllocator allocator(test_mem, sizeof(test_mem));
ScopeStack outerScope(allocator);
Foo* foo0 = outerScope.newObject<Foo>();
{
ScopeStack innerScope(allocator);
Foo* foo1 = innerScope.newObject<Foo>();
Foo* foo2 = innerScope.newObject<Foo>();
}
return 0;
}
@chorakm
Copy link

chorakm commented Dec 31, 2014

Unless I am somehow mistaken line 24 is incorrect. m_ptr is incremented by size before assigning to result on my machine. wouldn't you want to return the current m_ptr location and then increment m_ptr by size? The slides showed this too and I didn't understand it, maybe it was just an error. Your test wouldn't show the bug because all your objects are the same size, but if they were different sizes I think you'd have memory corruption.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment