From edadf58a914951201278d52ae05e515164b6f8b7 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Thu, 15 Oct 2015 05:06:30 +0300 Subject: [PATCH] Some tests for HeapBasedPoolAllocator<>; the concurrency test is failing with heap corruption --- .../helpers/heap_based_pool_allocator.cpp | 132 ++++++++++++++---- 1 file changed, 108 insertions(+), 24 deletions(-) diff --git a/libuavcan/test/helpers/heap_based_pool_allocator.cpp b/libuavcan/test/helpers/heap_based_pool_allocator.cpp index 93da37130f..61daf378d8 100644 --- a/libuavcan/test/helpers/heap_based_pool_allocator.cpp +++ b/libuavcan/test/helpers/heap_based_pool_allocator.cpp @@ -4,47 +4,131 @@ #include #include +#include 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 allocator; + std::cout << ">>> HEAP BEFORE:" << std::endl; + malloc_stats(); - ASSERT_EQ(0, allocator.getNumCachedBlocks()); + uavcan::HeapBasedPoolAllocator 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 + +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 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