The setup<T>() helper allocates two buffers (mem1, mem2) and returns them as std::pair<T*, T*>.
In non-overlapping tests, both buffers are correctly freed:
auto [ptr1, ptr2] = setup<trivially_relocatable_struct>();
// ... test ...
std::free(ptr1);
std::free(ptr2); // ✓ both freed
However, in overlapping tests, the second buffer is captured but never freed:
auto [ptr, ___] = setup<trivially_relocatable_struct_overlapping>();
// ... test uses only ptr ...
std::free(ptr); // mem2 is leaked
Since overlapping tests only use a single buffer (in-place relocation), the second allocation is unnecessary and gets leaked every time.
Impact
~20 leak sites across multiple relocation test files
Each leak allocates N * sizeof(T) (N = 500)
Repeats across test runs, leading to unnecessary memory usage
Root Cause
setup<T>() was designed for non-overlapping tests that require two buffers.
Overlapping tests reuse a single buffer but still call the same helper, which allocates a second buffer that is never used or freed.
The placeholder binding (___) makes it clear the second value is intentionally ignored, but the allocation still happens.
Affected Areas
uninitialized_relocate.cpp
uninitialized_relocaten.cpp
uninitialized_relocate_backward.cpp
uninitialized_relocate_sender.cpp
uninitialized_relocaten_sender.cpp
uninitialized_relocate_backward_sender.cpp
(affecting test_overlapping() and test_right_overlapping() blocks)
Proposed Fix
Option A — Free the unused buffer (quick fix)
auto [ptr, ptr2_unused] = setup<trivially_relocatable_struct_overlapping>();
// ... test ...
std::free(ptr);
std::free(ptr2_unused);
Option B — Introduce a single-buffer helper (preferred)
template <typename T>
T* setup_single()
{
clear();
void* mem = std::malloc(N * sizeof(T));
HPX_TEST(mem);
T* ptr = static_cast<T*>(mem);
for (int i = 0; i < N; i++)
hpx::construct_at(ptr + i, i);
HPX_TEST(T::made.size() == N);
return ptr;
}
Then update overlapping tests to use:
auto ptr = setup_single<trivially_relocatable_struct_overlapping>();
// ... test ...
std::free(ptr);
This avoids unnecessary allocation, makes ownership explicit, and eliminates the risk of leaks.
Happy to submit a PR if this approach looks good
The
setup<T>()helper allocates two buffers (mem1,mem2) and returns them asstd::pair<T*, T*>.In non-overlapping tests, both buffers are correctly freed: