mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-15 15:03:34 +08:00
Some tests for HeapBasedPoolAllocator<>; the concurrency test is failing with heap corruption
This commit is contained in:
@@ -4,47 +4,131 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
|
||||
#include <malloc.h>
|
||||
|
||||
inline bool atomicCompareAndSwap(void** address, void* expected, void* replacement)
|
||||
{
|
||||
return __sync_bool_compare_and_swap(address, expected, replacement);
|
||||
}
|
||||
|
||||
TEST(ThreadSafeHeapBasedPoolAllocator, Basic)
|
||||
TEST(HeapBasedPoolAllocator, Basic)
|
||||
{
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> allocator;
|
||||
std::cout << ">>> HEAP BEFORE:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
ASSERT_EQ(0, allocator.getNumCachedBlocks());
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> al;
|
||||
|
||||
void* a = allocator.allocate(10);
|
||||
void* b = allocator.allocate(10);
|
||||
void* c = allocator.allocate(10);
|
||||
void* d = allocator.allocate(10);
|
||||
ASSERT_EQ(0, al.getNumCachedBlocks());
|
||||
|
||||
ASSERT_EQ(0, allocator.getNumCachedBlocks());
|
||||
void* a = al.allocate(10);
|
||||
void* b = al.allocate(10);
|
||||
void* c = al.allocate(10);
|
||||
void* d = al.allocate(10);
|
||||
|
||||
allocator.deallocate(a);
|
||||
ASSERT_EQ(1, allocator.getNumCachedBlocks());
|
||||
ASSERT_EQ(0, al.getNumCachedBlocks());
|
||||
|
||||
allocator.deallocate(b);
|
||||
ASSERT_EQ(2, allocator.getNumCachedBlocks());
|
||||
al.deallocate(a);
|
||||
ASSERT_EQ(1, al.getNumCachedBlocks());
|
||||
|
||||
allocator.deallocate(c);
|
||||
ASSERT_EQ(3, allocator.getNumCachedBlocks());
|
||||
al.deallocate(b);
|
||||
ASSERT_EQ(2, al.getNumCachedBlocks());
|
||||
|
||||
a = allocator.allocate(10);
|
||||
ASSERT_EQ(2, allocator.getNumCachedBlocks());
|
||||
al.deallocate(c);
|
||||
ASSERT_EQ(3, al.getNumCachedBlocks());
|
||||
|
||||
a = al.allocate(10);
|
||||
ASSERT_EQ(2, al.getNumCachedBlocks());
|
||||
ASSERT_EQ(c, a);
|
||||
|
||||
allocator.deallocate(a);
|
||||
ASSERT_EQ(3, allocator.getNumCachedBlocks());
|
||||
al.deallocate(a);
|
||||
ASSERT_EQ(3, al.getNumCachedBlocks());
|
||||
|
||||
allocator.shrink();
|
||||
ASSERT_EQ(0, allocator.getNumCachedBlocks());
|
||||
al.shrink();
|
||||
ASSERT_EQ(0, al.getNumCachedBlocks());
|
||||
|
||||
allocator.deallocate(d);
|
||||
ASSERT_EQ(1, allocator.getNumCachedBlocks());
|
||||
al.deallocate(d);
|
||||
ASSERT_EQ(1, al.getNumCachedBlocks());
|
||||
|
||||
allocator.shrink();
|
||||
ASSERT_EQ(0, allocator.getNumCachedBlocks());
|
||||
al.shrink();
|
||||
ASSERT_EQ(0, al.getNumCachedBlocks());
|
||||
|
||||
std::cout << ">>> HEAP AFTER:" << std::endl;
|
||||
malloc_stats();
|
||||
}
|
||||
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
|
||||
#include <thread>
|
||||
|
||||
inline bool atomicCompareAndSwapWithRescheduling(void** address, void* expected, void* replacement)
|
||||
{
|
||||
/*
|
||||
* Yield is added for testing reasons, making CAS failure more likely
|
||||
*/
|
||||
if ((float(std::rand()) / float(RAND_MAX)) < 0.05)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
return __sync_bool_compare_and_swap(address, expected, replacement);
|
||||
}
|
||||
|
||||
TEST(HeapBasedPoolAllocator, Concurrency)
|
||||
{
|
||||
std::cout << ">>> HEAP BEFORE:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwapWithRescheduling> al;
|
||||
|
||||
volatile bool terminate = false;
|
||||
|
||||
/*
|
||||
* Starting the testing threads
|
||||
*/
|
||||
std::thread threads[3];
|
||||
|
||||
for (auto& x : threads)
|
||||
{
|
||||
x = std::thread([&al, &terminate]()
|
||||
{
|
||||
while (!terminate)
|
||||
{
|
||||
auto a = al.allocate(1);
|
||||
auto b = al.allocate(1);
|
||||
auto c = al.allocate(1);
|
||||
al.deallocate(al.allocate(1));
|
||||
al.deallocate(a);
|
||||
al.deallocate(b);
|
||||
al.deallocate(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Running the threads for some time, then terminating
|
||||
*/
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
terminate = true;
|
||||
std::cout << "Terminating workers..." << std::endl;
|
||||
|
||||
for (auto& x : threads)
|
||||
{
|
||||
x.join();
|
||||
}
|
||||
std::cout << "All workers joined" << std::endl;
|
||||
|
||||
/*
|
||||
* Now, there must not be any leaked memory, because the worker threads deallocate everything before completion.
|
||||
*/
|
||||
//std::cout << "Cached blocks: " << al.getNumCachedBlocks() << std::endl;
|
||||
|
||||
std::cout << ">>> HEAP BEFORE SHRINK:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
al.shrink();
|
||||
|
||||
std::cout << ">>> HEAP AFTER SHRINK:" << std::endl;
|
||||
malloc_stats();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user