1

I'm writing a simple memory arena allocator and facing a small problem with exception safety. The situation is when you allocate an object which itself calls the allocator. The objective of the memory pool is to allocate a bunch of objects at one time, and then delete them all when the pool is destroyed.

{
    MemoryArena m;
    std::string* ptr = m.Allocate<std::string>();
    // use ptr whatever
    // Cleaned up when pool is destroyed
}

But this gets rather nasty when it's used multiple times. If the inner allocation is cleaned up, then it could be used afterwards- not a bad assumption, since it's the definition of the pool to never delete objects until it's lifetime is over. consider:

struct X {
    X(MemoryArena* ptr, std::string*& ref) {
        ref = ptr->Allocate<std::string>();
        throw std::runtime_error("hai");
    }
};
MemoryArena m;
std::string* ptr;
m.Allocate<X>(&m, ptr);
// ptr is invalid- even though it came from the arena 
// which hasn't yet been destroyed

But if the inner allocation isn't cleaned up, the outer allocation also can't be cleaned up, because the memory arena allocates them linearly like on a hardware stack, so I leak memory. So either I violate my semantics by destroying an object early, or I leak memory.

Any suggestions for how to resolve this problem?

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 2
    What is `MemoryArena`? How can we say anything without knowing it? – Nawaz Dec 21 '11 at 15:19
  • It's unclear how you leak memory, when it will be freed when the arena is freed. – zvrba Dec 21 '11 at 15:27
  • @zvrba: It's allocated, but unusable, because the object was never constructed and the pointer never returned, so the memory is leaked until the region is destroyed. – Puppy Dec 21 '11 at 15:27
  • 3
    @DeadMG: What is the implementation of `MemoryArena`? That information is not in the question. – Nawaz Dec 21 '11 at 15:32
  • 1
    No implementation is needed. This looks like a design question to me. Basically asking how can it be implemented without violating some semantics nor leaking memory. – R. Martinho Fernandes Dec 21 '11 at 15:34
  • @Nawaz: It's unnecessary. The problem is endemic to all memory arenas, not any specific implementation of them. – Puppy Dec 21 '11 at 15:36
  • @SteveJessop why are you using the comments as a draft space for your answer? ;) – R. Martinho Fernandes Dec 21 '11 at 17:54
  • @ R. Martinho Fernandes: heh, I was hoping to get a response from DeadMG as to whether my "great insight" was actually relevant to the problem in general, or just to this particular example code and hence not very interesting. Tell you what, it's been long enough, I'll change them to an answer. – Steve Jessop Dec 21 '11 at 18:09
  • Have you looked at [Boost.Pool](http://www.boost.org/libs/pool/)? I'm sure its implementation had to deal with similar issues... – ildjarn Dec 21 '11 at 18:13
  • @ildjarn: It's not really similar, because a pool can delete any object at any time. – Puppy Dec 21 '11 at 22:57

2 Answers2

2

Maybe it's just the example code that this applies to, but I don't think the user should assume that ptr is valid when the constructor of X throws. It could just as well have thrown before ref was assigned.

So I'd say clean up the inner object if you can (i.e. if the front of the arena currently lies at the end of the inner object). OK, an allocation from the arena becomes invalid, which isn't normal, but it's an allocation that should never had made it out into the real world anyway.

Perhaps you could make this explicit, with a concept of a "soft" allocation. It's not guaranteed to live forever, because while still "soft" it can be freed back to the arena. Then X's constructor would do something like:

SoftPtr<std::string> tmp(ptr->SoftAllocate<std::string>());
stuff_that_might_throw(); 
ref = tmp.release();

Executing the destructor of SoftPtr without without first calling release implies that no reference to the object has been exposed. It calls a function of MemoryArena that does something like:

  • destruct the object
  • check whether this is the most recent allocation from the arena
    • if so, subtract the size from the arena's current position pointer
    • if not, do nothing else (the memory is leaked)

So any number of allocs can be "backed out", provided it's done in reverse order.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
0

By the very semantics of memory pools, and as you have stated yourself in the question, only the pool as a whole can be freed, not individual objects. Yet you want to do exactly that.

A possibility is to equip the allocator with brk-like functions to get and set the next allocation address. This gives you a low-level mechanism that you can use to built whatever you want on top of it.

zvrba
  • 24,186
  • 3
  • 55
  • 65