In my project I use a class for paged memory allocation. This class uses a struct to store all of its allocations:
enum PageStatus : uint_fast8_t { //!< possible use stati of an allocated page
PAGE_STATUS_INVALID = 0b00,
PAGE_STATUS_FREE = 0b01, //!< the page is free
PAGE_STATUS_USED = 0b10, //!< the page is (partially) used
};
struct PhysicalPage { //!< represents a page that has been allocated
char* pData; //!< pointer to the allocation
PageStatus status; //!< status of the allocation
};
These PhysicalPage
s are stored in a vector std::vector<PhysicalPage> physicalAllocations {};
.
During runtime pages are added to the vector and some may be removed. During the removal the last element is popped and the memory is returned using delete page.pData
. However a problem occurs when the allocator class reaches end of life and gets deallocated from the stack. When pyhsicalAllocations
's vector destructor is called it tries to destruct not only the elements themself but also the reserved memory (which the vector keeps as a buffer for when the size is changed). That causes invalid memory pointers to be deleted, stopping program execution:
double free or corruption (!prev)
Signal: SIGABRT (Aborted)
It's probably also worth mentioning that allocations are done in chunks larger than pages, which means that only one in every x pointers are actually valid allocations. All other pointers are just offsets from the actual memory locations.
To prevent the error from occurring I tried:
deleting manually (this is a bit overcomplicated due to chunked allocation)
for (size_t i = physicalAllocations.size(); 0 < i; i -= 1 << allocationChunkSize) {
delete physicalAllocations[i - (1 << allocationChunkSize)].pData;
for (size_t a = 0; a < 1 << allocationChunkSize; a )
physicalAllocations.pop_back();
}
clearing the vector
physicalAllocations.clear();
swapping for a clear vector
std::vector<PhysicalPage>(0).swap(physicalAllocations);
of which none worked.
I've been working on this problem for a lot longer that I would like to admit and your help is very much appreciated. Thanks!
CodePudding user response:
std::shared_ptr<char[]> pData
and its aliasing constructor (8) might help. (that might even allow to get rid of PageStatus).
It would look something like:
constexpr std::size_t page_size = 6;
struct PhysicalPage {
std::shared_ptr<char[]> pData;
};
int main()
{
std::vector<PhysicalPage> pages;
{
std::shared_ptr<char[]> big_alloc = std::unique_ptr<char[]>(new char[42]{"hello world. 4 8 15 16 23 42"});
for (std::size_t i = 0; i != 42 / page_size; i) {
pages.push_back(PhysicalPage{std::shared_ptr<char[]>{big_alloc, big_alloc.get() i * page_size}});
}
}
pages.erase(pages.begin());
pages.erase(pages.begin() 2);
for (auto& p : pages) {
std::cout << std::string_view(p.pData.get(), page_size) << std::endl;
}
}