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