Skip to content

Commit afe870c

Browse files
emersonknappdirk-thomas
authored andcommitted
Static Allocator - Aligned allocations (#31)
* Aligned memory pool allocations Signed-off-by: Emerson Knapp <[email protected]> * Fix formatting Signed-off-by: Emerson Knapp <[email protected]> * Address some review nitpicks Signed-off-by: Emerson Knapp <[email protected]> * Fix formatting Signed-off-by: Emerson Knapp <[email protected]> * A pass at adding unit test for StaticAllocator Signed-off-by: Emerson Knapp <[email protected]>
1 parent ba05291 commit afe870c

File tree

6 files changed

+67
-35
lines changed

6 files changed

+67
-35
lines changed

osrf_testing_tools_cpp/src/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ add_subdirectory(test_runner)
33

44
set(memory_tools_extra_test_env "${memory_tools_extra_test_env}" PARENT_SCOPE)
55
set(memory_tools_is_available "${memory_tools_is_available}" PARENT_SCOPE)
6+
set(memory_tools_src_dir_internal_testing_only
7+
"${memory_tools_src_dir_internal_testing_only}" PARENT_SCOPE)

osrf_testing_tools_cpp/src/memory_tools/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,5 @@ install(EXPORT memory_tools_interpose
9696

9797
set(memory_tools_extra_test_env "${memory_tools_extra_test_env}" PARENT_SCOPE)
9898
set(memory_tools_is_available "${memory_tools_is_available}" PARENT_SCOPE)
99+
set(memory_tools_src_dir_internal_testing_only
100+
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>" PARENT_SCOPE)

osrf_testing_tools_cpp/src/memory_tools/impl/linux.cpp

+19-24
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,23 @@ find_original_function(const char * name)
4242
return original_function;
4343
}
4444

45+
// An amount of memory that is greater than what is needed for static initialization
46+
// for any test we run. It was found experimentally on Ubuntu Linux 16.04 x86_64.
47+
static constexpr size_t STATIC_ALLOCATOR_SIZE = 0x800000;
4548
using osrf_testing_tools_cpp::memory_tools::impl::StaticAllocator;
46-
// the size was found experimentally on Ubuntu Linux 16.04 x86_64
47-
using StaticAllocatorT = StaticAllocator<8388608>;
48-
// used to fullfil calloc call from dlerror.c during initialization of original functions
49-
// constructor is called on first use with a placement-new and the static storage
49+
using StaticAllocatorT = StaticAllocator<STATIC_ALLOCATOR_SIZE>;
5050
static uint8_t g_static_allocator_storage[sizeof(StaticAllocatorT)];
51-
static StaticAllocatorT * g_static_allocator = nullptr;
51+
52+
// Contains global allocator to make 100% sure to avoid Static Initialization Order Fiasco.
53+
// "Construct on first use" idiom
54+
static StaticAllocatorT *
55+
get_static_allocator()
56+
{
57+
// placement-new the static allocator in preallocated storage
58+
// which is used while finding the original memory functions
59+
static StaticAllocatorT * alloc = new (g_static_allocator_storage) StaticAllocatorT;
60+
return alloc;
61+
}
5262

5363
// storage for original malloc/realloc/calloc/free
5464
using MallocSignature = void * (*)(size_t);
@@ -78,12 +88,7 @@ void *
7888
malloc(size_t size) noexcept
7989
{
8090
if (!get_static_initialization_complete()) {
81-
if (nullptr == g_static_allocator) {
82-
// placement-new the static allocator
83-
// which is used while finding the original memory functions
84-
g_static_allocator = new (g_static_allocator_storage) StaticAllocatorT;
85-
}
86-
return g_static_allocator->allocate(size);
91+
return get_static_allocator()->allocate(size);
8792
}
8893
return unix_replacement_malloc(size, g_original_malloc);
8994
}
@@ -92,12 +97,7 @@ void *
9297
realloc(void * pointer, size_t size) noexcept
9398
{
9499
if (!get_static_initialization_complete()) {
95-
if (nullptr == g_static_allocator) {
96-
// placement-new the static allocator
97-
// which is used while finding the original memory functions
98-
g_static_allocator = new (g_static_allocator_storage) StaticAllocatorT;
99-
}
100-
return g_static_allocator->reallocate(pointer, size);
100+
return get_static_allocator()->reallocate(pointer, size);
101101
}
102102
return unix_replacement_realloc(pointer, size, g_original_realloc);
103103
}
@@ -106,20 +106,15 @@ void *
106106
calloc(size_t count, size_t size) noexcept
107107
{
108108
if (!get_static_initialization_complete()) {
109-
if (nullptr == g_static_allocator) {
110-
// placement-new the static allocator
111-
// which is used while finding the original memory functions
112-
g_static_allocator = new (g_static_allocator_storage) StaticAllocatorT;
113-
}
114-
return g_static_allocator->zero_allocate(count, size);
109+
return get_static_allocator()->zero_allocate(count, size);
115110
}
116111
return unix_replacement_calloc(count, size, g_original_calloc);
117112
}
118113

119114
void
120115
free(void * pointer) noexcept
121116
{
122-
if (nullptr == pointer || g_static_allocator->deallocate(pointer)) {
117+
if (nullptr == pointer || get_static_allocator()->deallocate(pointer)) {
123118
// free of nullptr or,
124119
// memory was originally allocated by static allocator, no need to pass to "real" free
125120
return;

osrf_testing_tools_cpp/src/memory_tools/impl/static_allocator.hpp

+19-11
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ namespace memory_tools
2929
namespace impl
3030
{
3131

32+
// Alignment of the largest primitive type for this system.
33+
static constexpr size_t MAX_ALIGN = alignof(std::max_align_t);
34+
35+
/// Round value up to a multiple of alignment.
36+
/**
37+
* Implementation cribbed from Boost.
38+
* https://github.com/boostorg/align/blob/develop/include/boost/align/align_up.hpp
39+
*/
40+
static constexpr inline std::size_t
41+
align_up(std::size_t value, std::size_t alignment) noexcept
42+
{
43+
return (value + alignment - 1) & ~(alignment - 1);
44+
}
45+
3246
template<size_t MemoryPoolSize>
3347
class StaticAllocator
3448
{
@@ -45,9 +59,10 @@ class StaticAllocator
4559
void *
4660
allocate(size_t size)
4761
{
48-
if (size <= size_t(std::distance(stack_pointer_, end_))) {
62+
const size_t aligned_size = align_up(size, MAX_ALIGN);
63+
if (aligned_size <= static_cast<size_t>(std::distance(end_, stack_pointer_))) {
4964
uint8_t * result = stack_pointer_;
50-
stack_pointer_ += size;
65+
stack_pointer_ += aligned_size;
5166
return result;
5267
}
5368
SAFE_FWRITE(stderr, "StackAllocator.allocate() -> nullptr\n");
@@ -105,7 +120,8 @@ class StaticAllocator
105120
}
106121

107122
private:
108-
uint8_t memory_pool_[MemoryPoolSize];
123+
// Make sure that our memory pool is aligned to the maximum primitive size for this system.
124+
alignas(MAX_ALIGN) uint8_t memory_pool_[MemoryPoolSize];
109125
uint8_t * begin_;
110126
uint8_t * end_;
111127
uint8_t * stack_pointer_;
@@ -115,12 +131,4 @@ class StaticAllocator
115131
} // namespace memory_tools
116132
} // namespace osrf_testing_tools_cpp
117133

118-
int main(void)
119-
{
120-
osrf_testing_tools_cpp::memory_tools::impl::StaticAllocator<64> sa;
121-
void * mem = sa.allocate(16);
122-
(void)mem;
123-
return 0;
124-
}
125-
126134
#endif // MEMORY_TOOLS__IMPL__STATIC_ALLOCATOR_HPP_

osrf_testing_tools_cpp/test/memory_tools/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ target_link_libraries(test_memory_tools
44
memory_tools
55
gtest_main
66
)
7+
target_include_directories(test_memory_tools
8+
PRIVATE ${memory_tools_src_dir_internal_testing_only})
79

810
if(memory_tools_is_available)
911
add_test(

osrf_testing_tools_cpp/test/memory_tools/test_memory_tools.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "osrf_testing_tools_cpp/memory_tools/memory_tools.hpp"
2121
#include "osrf_testing_tools_cpp/scope_exit.hpp"
22+
#include "memory_tools/impl/static_allocator.hpp"
2223

2324
/**
2425
* Tests the dynamic memory checking tools.
@@ -232,3 +233,25 @@ TEST(TestMemoryTools, test_example) {
232233
});
233234
t2.join();
234235
}
236+
237+
/**
238+
* Tests the static allocator used during dynamic library loading.
239+
*/
240+
TEST(TestMemoryTools, test_static_allocation_alignment) {
241+
static constexpr size_t allocator_size = 0x1000;
242+
// Arbitrarily chosen values (somewhat observed values from OpenSSL static initialization).
243+
// Not all aligned to std::max_align_t on most platforms.
244+
static const std::vector<size_t> request_sizes = {
245+
24, 24, 24, 7, 82, 2424
246+
};
247+
static constexpr size_t max_align = alignof(std::max_align_t);
248+
249+
osrf_testing_tools_cpp::memory_tools::impl::StaticAllocator<allocator_size> allocator;
250+
251+
// Check that all returned memory blocks are aligned with the max_align type.
252+
for (const size_t request : request_sizes) {
253+
void * memory = allocator.allocate(request);
254+
ASSERT_EQ(reinterpret_cast<size_t>(memory) % max_align, size_t(0));
255+
ASSERT_TRUE(allocator.deallocate(memory));
256+
}
257+
}

0 commit comments

Comments
 (0)