1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//===- llvm/unittest/Support/ThreadSafeAllocatorTest.cpp ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/ThreadSafeAllocator.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/ThreadPool.h"
#include "gtest/gtest.h"
#include <atomic>
#include <thread>
using namespace llvm;
namespace {
struct AllocCondition {
std::mutex BusyLock, EndLock;
std::condition_variable Busy, End;
bool IsBusy = false, IsEnd = false;
std::atomic<unsigned> BytesAllocated = 0;
void startAllocation() {
{
std::lock_guard<std::mutex> Lock(BusyLock);
IsBusy = true;
}
Busy.notify_all();
}
void waitAllocationStarted() {
std::unique_lock<std::mutex> LBusy(BusyLock);
Busy.wait(LBusy, [&]() { return IsBusy; });
IsBusy = false;
}
void finishAllocation() {
{
std::lock_guard<std::mutex> Lock(EndLock);
IsEnd = true;
}
End.notify_all();
}
void waitAllocationFinished() {
std::unique_lock<std::mutex> LEnd(EndLock);
// Wait for end state.
End.wait(LEnd, [&]() { return IsEnd; });
IsEnd = false;
}
};
class MockAllocator : public AllocatorBase<MockAllocator> {
public:
MockAllocator() = default;
void *Allocate(size_t Size, size_t Alignment) {
C.startAllocation();
C.waitAllocationFinished();
C.BytesAllocated += Size;
return Reserved;
}
AllocCondition &getAllocCondition() { return C; }
private:
char Reserved[16];
AllocCondition C;
};
} // namespace
#if (LLVM_ENABLE_THREADS)
TEST(ThreadSafeAllocatorTest, AllocWait) {
ThreadSafeAllocator<MockAllocator> Alloc;
AllocCondition *C;
// Get the allocation from the allocator first since this requires a lock.
Alloc.applyLocked(
[&](MockAllocator &Alloc) { C = &Alloc.getAllocCondition(); });
DefaultThreadPool Threads;
// First allocation of 1 byte.
Threads.async([&Alloc]() {
char *P = (char *)Alloc.Allocate(1, alignof(char));
P[0] = 0;
});
// No allocation yet.
EXPECT_EQ(C->BytesAllocated, 0u);
C->waitAllocationStarted(); // wait till 1st alloocation starts.
// Second allocation of 2 bytes.
Threads.async([&Alloc]() {
char *P = (char *)Alloc.Allocate(2, alignof(char));
P[1] = 0;
});
C->finishAllocation(); // finish 1st allocation.
C->waitAllocationStarted(); // wait till 2nd allocation starts.
// still 1 byte allocated since 2nd allocation is not finished yet.
EXPECT_EQ(C->BytesAllocated, 1u);
C->finishAllocation(); // finish 2nd allocation.
Threads.wait(); // all allocations done.
EXPECT_EQ(C->BytesAllocated, 3u);
}
TEST(ThreadSafeAllocatorTest, AllocWithAlign) {
ThreadSafeAllocator<BumpPtrAllocator> Alloc;
DefaultThreadPool Threads;
for (unsigned Index = 1; Index < 100; ++Index)
Threads.async(
[&Alloc](unsigned I) {
int *P = (int *)Alloc.Allocate(sizeof(int) * I, alignof(int));
P[I - 1] = I;
},
Index);
Threads.wait();
Alloc.applyLocked([](BumpPtrAllocator &Alloc) {
EXPECT_EQ(4950U * sizeof(int), Alloc.getBytesAllocated());
});
}
TEST(ThreadSafeAllocatorTest, SpecificBumpPtrAllocator) {
ThreadSafeAllocator<SpecificBumpPtrAllocator<int>> Alloc;
DefaultThreadPool Threads;
for (unsigned Index = 1; Index < 100; ++Index)
Threads.async(
[&Alloc](unsigned I) {
int *P = Alloc.Allocate(I);
P[I - 1] = I;
},
Index);
Threads.wait();
}
#endif
|