问题
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