<commit_before>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 */

#include <gtest/gtest.h>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>

inline bool atomicCompareAndSwap(void** address, void* expected, void* replacement)
{
    return __sync_bool_compare_and_swap(address, expected, replacement);
}

TEST(ThreadSafeHeapBasedPoolAllocator, Basic)
{
    uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> allocator;

    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    void* a = allocator.allocate(10);
    void* b = allocator.allocate(10);
    void* c = allocator.allocate(10);
    void* d = allocator.allocate(10);

    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    allocator.deallocate(a);
    ASSERT_EQ(1, allocator.getNumCachedBlocks());

    allocator.deallocate(b);
    ASSERT_EQ(2, allocator.getNumCachedBlocks());

    allocator.deallocate(c);
    ASSERT_EQ(3, allocator.getNumCachedBlocks());

    a = allocator.allocate(10);
    ASSERT_EQ(2, allocator.getNumCachedBlocks());
    ASSERT_EQ(c, a);

    allocator.deallocate(a);
    ASSERT_EQ(3, allocator.getNumCachedBlocks());

    allocator.shrink();
    ASSERT_EQ(0, allocator.getNumCachedBlocks());

    allocator.deallocate(d);
    ASSERT_EQ(1, allocator.getNumCachedBlocks());

    allocator.shrink();
    ASSERT_EQ(0, allocator.getNumCachedBlocks());
}<commit_msg>Some tests for HeapBasedPoolAllocator<>; the concurrency test is failing with heap corruption<commit_after>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 */

#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(HeapBasedPoolAllocator, Basic)
{
    std::cout << ">>> HEAP BEFORE:" << std::endl;
    malloc_stats();

    uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, atomicCompareAndSwap> al;

    ASSERT_EQ(0, al.getNumCachedBlocks());

    void* a = al.allocate(10);
    void* b = al.allocate(10);
    void* c = al.allocate(10);
    void* d = al.allocate(10);

    ASSERT_EQ(0, al.getNumCachedBlocks());

    al.deallocate(a);
    ASSERT_EQ(1, al.getNumCachedBlocks());

    al.deallocate(b);
    ASSERT_EQ(2, al.getNumCachedBlocks());

    al.deallocate(c);
    ASSERT_EQ(3, al.getNumCachedBlocks());

    a = al.allocate(10);
    ASSERT_EQ(2, al.getNumCachedBlocks());
    ASSERT_EQ(c, a);

    al.deallocate(a);
    ASSERT_EQ(3, al.getNumCachedBlocks());

    al.shrink();
    ASSERT_EQ(0, al.getNumCachedBlocks());

    al.deallocate(d);
    ASSERT_EQ(1, al.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
{'fraction_non_alphanumeric': 0.14651162790697675, 'fraction_numerical': 0.012473572938689217, 'mean_word_length': 3.4009302325581396, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5102469', 'n_tokens_mistral': 1667, 'n_tokens_neox': 1564, 'n_words': 298}