diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 5d5b01296..08408ee93 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -14,7 +14,7 @@ mkclass_target(sysloglogger.ti sysloglogger-ti.cpp sysloglogger-ti.hpp) set(base_SOURCES i2-base.hpp - allocator.cpp + allocator.cpp allocator.hpp application.cpp application.hpp application-ti.hpp application-version.cpp application-environment.cpp array.cpp array.hpp array-script.cpp atomic.hpp diff --git a/lib/base/allocator.cpp b/lib/base/allocator.cpp index a5062a48f..09b2beed0 100644 --- a/lib/base/allocator.cpp +++ b/lib/base/allocator.cpp @@ -2,19 +2,99 @@ #ifndef _WIN32 +#include "base/allocator.hpp" +#include +#include #include #include +#include +#include + +using namespace icinga; + +struct DAPage +{ + size_t TotalSize = sizeof(DAPage); + DAPage* Next = nullptr; + max_align_t* MemoryBegin = nullptr; + max_align_t* MemoryEnd = nullptr; + max_align_t Memory[1] = {}; +}; + +static thread_local struct { + unsigned int InUse = 0; + DAPage* TopPage = nullptr; +} l_DefragAllocator; extern "C" void* malloc(size_t bytes) { static const auto libcMalloc = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc"); - return libcMalloc(bytes); + static const size_t pageSize = std::max(128L * 1024L, sysconf(_SC_PAGESIZE)); + + if (BOOST_LIKELY(!l_DefragAllocator.InUse)) { + return libcMalloc(bytes); + } + + if (BOOST_UNLIKELY(!bytes)) { + return nullptr; + } + + auto chunks ((bytes + sizeof(max_align_t) - 1u) / sizeof(max_align_t)); + + if (BOOST_UNLIKELY(!l_DefragAllocator.TopPage || l_DefragAllocator.TopPage->MemoryEnd - l_DefragAllocator.TopPage->MemoryBegin < chunks)) { + auto total ((sizeof(DAPage) + sizeof(max_align_t) * (chunks - 1u) + pageSize - 1u) / pageSize * pageSize); + + auto nextPage (mmap(nullptr, total, PROT_READ | PROT_WRITE, MAP_ANONYMOUS +#ifdef MAP_HASSEMAPHORE + | MAP_HASSEMAPHORE +#endif + | MAP_PRIVATE, -1, 0)); + + if (BOOST_UNLIKELY(nextPage == MAP_FAILED)) { + return nullptr; + } + + auto p ((DAPage*)nextPage); + + p->TotalSize = total; + p->Next = l_DefragAllocator.TopPage; + p->MemoryBegin = p->Memory; + p->MemoryEnd = p->MemoryBegin + chunks; + + l_DefragAllocator.TopPage = p; + } + + auto memory ((void*)l_DefragAllocator.TopPage->MemoryBegin); + + l_DefragAllocator.TopPage->MemoryBegin += chunks; + + return memory; } extern "C" void free(void* memory) { static const auto libcFree = (void(*)(void*))dlsym(RTLD_NEXT, "free"); - libcFree(memory); + + if (BOOST_LIKELY(!l_DefragAllocator.InUse)) { + libcFree(memory); + } +} + +DefragAllocator::DefragAllocator() +{ + ++l_DefragAllocator.InUse; +} + +DefragAllocator::~DefragAllocator() +{ + if (!--l_DefragAllocator.InUse) { + while (l_DefragAllocator.TopPage) { + auto next (l_DefragAllocator.TopPage->Next); + + munmap(l_DefragAllocator.TopPage, l_DefragAllocator.TopPage->TotalSize); + l_DefragAllocator.TopPage = next; + } + } } #endif /* _WIN32 */ diff --git a/lib/base/allocator.hpp b/lib/base/allocator.hpp new file mode 100644 index 000000000..a179b881b --- /dev/null +++ b/lib/base/allocator.hpp @@ -0,0 +1,24 @@ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ + +#pragma once + +namespace icinga +{ + +/** + * TODO + * + * @ingroup base + */ +class DefragAllocator +{ +public: + DefragAllocator(); + DefragAllocator(const DefragAllocator&) = delete; + DefragAllocator(DefragAllocator&&) = delete; + DefragAllocator& operator=(const DefragAllocator&) = delete; + DefragAllocator& operator=(DefragAllocator&&) = delete; + ~DefragAllocator(); +}; + +}