Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/memtailor/Arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ namespace memt {
MEMT_ASSERT(size >= needed);
MEMT_ASSERT(size % MemoryAlignment == 0);
_blocks.allocBlock(size);

// guardPoint() and freeTopFromOldBlock() both require that a block
// behind the front block is not empty.
if (block().hasPreviousBlock() && block().previousBlock()->empty())
_blocks.freePreviousBlock();
}

void Arena::freeTopFromOldBlock(void* ptr) {
Expand Down
25 changes: 19 additions & 6 deletions src/memtailor/Arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,28 @@ namespace memt {
return;
}

// The blocks allocated since the guard point have to be deallocated,
// as guardPoint() and freeTopFromOldBlock() both rely on a block
// behind the front block never being empty.
Block* b = &block();
while (!(b->begin() < ptr && ptr <= b->end())) {
if (!(b->begin() < ptr && ptr <= b->end())) {
b->setPosition(b->begin());
b = b->previousBlock();
do {
// If you get an assert here then most likely the guard point was
// invalidated by deallocating memory that was live at the point
// the guard point was created.
MEMT_ASSERT(b->hasPreviousBlock());
b = b->previousBlock();
} while (!(b->begin() < ptr && ptr <= b->end()));

// If you get an assert here then most likely the guard point was
// invalidated by deallocating memory that was live at the point
// the guard point was created.
MEMT_ASSERT(b != 0);
#ifdef MEMT_DEBUG
// Drop the record of the allocations in the blocks that are about to
// be freed while their addresses are still valid.
while (!_debugAllocs.empty() && !b->isInBlock(_debugAllocs.back()))
_debugAllocs.pop_back();
#endif
while (block().previousBlock() != b)
_blocks.freePreviousBlock();
}
MEMT_ASSERT(b->begin() < ptr && ptr <= b->end());
b->setPosition(ptr);
Expand Down
45 changes: 45 additions & 0 deletions src/test/ArenaTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,48 @@ TEST(Arena, GuardAfterEmptyRestore) {
#endif
memt::Arena::Guard guard2(arena); // must be possible on an empty arena
}

TEST(Arena, GuardFreesNewerBlocks) {
memt::Arena arena;
void* a = arena.alloc(1);
size_t memoryUse = 0;
{
memt::Arena::Guard guard(arena);
// Two new blocks, not one: with one, the only block left empty is
// the front block, which is allowed. With two there is also an empty
// block behind it, which is not.
arena.alloc(arena.getMemoryUse() + 1);
arena.alloc(arena.getMemoryUse() + 1);
memoryUse = arena.getMemoryUse();
}
// the block in the middle is deallocated. As for freeAndAllAfter, the
// front block is kept, so not all of the memory is released.
ASSERT_LT(arena.getMemoryUse(), memoryUse);

{
memt::Arena::Guard guard(arena); // requires no empty block behind the front
}

arena.freeTop(a); // a is the only allocation that is still live
ASSERT_TRUE(arena.isEmpty());
ASSERT_EQ(arena.getAllocatedMemoryUse(), 0u);
#ifdef MEMT_DEBUG
ASSERT_EQ(arena.debugAllocCount(), 0u);
#endif
}

TEST(Arena, EmptyBlockIsNotKeptBehindFrontBlock) {
memt::Arena arena;
void* p = arena.alloc(1);
void* q = arena.alloc(arena.getMemoryUse() + 1);
arena.freeTop(q);
void* r = arena.alloc(arena.getMemoryUse() + 1);
arena.freeTop(r);

arena.freeTop(p); // p is in the first block
ASSERT_TRUE(arena.isEmpty());
ASSERT_EQ(arena.getAllocatedMemoryUse(), 0u);
#ifdef MEMT_DEBUG
ASSERT_EQ(arena.debugAllocCount(), 0u);
#endif
}
Loading