Exception safety in memory arena

假如想象 提交于 2019-12-11 13:10:01

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/8592018/exception-safety-in-memory-arena

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!