xref: /aosp_15_r20/external/grpc-grpc/test/core/resource_quota/arena_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2017 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/lib/resource_quota/arena.h"
20 
21 #include <inttypes.h>
22 #include <string.h>
23 
24 #include <algorithm>
25 #include <iosfwd>
26 #include <memory>
27 #include <ostream>
28 #include <string>
29 #include <vector>
30 
31 #include "absl/strings/str_join.h"
32 #include "gtest/gtest.h"
33 
34 #include <grpc/support/sync.h>
35 #include <grpc/support/time.h>
36 
37 #include "src/core/lib/gprpp/ref_counted_ptr.h"
38 #include "src/core/lib/gprpp/thd.h"
39 #include "src/core/lib/iomgr/exec_ctx.h"
40 #include "src/core/lib/resource_quota/resource_quota.h"
41 #include "test/core/util/test_config.h"
42 
43 namespace grpc_core {
44 
45 struct AllocShape {
46   size_t initial_size;
47   std::vector<size_t> allocs;
48 };
49 
operator <<(std::ostream & out,const AllocShape & shape)50 std::ostream& operator<<(std::ostream& out, const AllocShape& shape) {
51   out << "AllocShape{initial_size=" << shape.initial_size
52       << ", allocs=" << absl::StrJoin(shape.allocs, ",") << "}";
53   return out;
54 }
55 
56 class AllocTest : public ::testing::TestWithParam<AllocShape> {};
57 
TEST_P(AllocTest,Works)58 TEST_P(AllocTest, Works) {
59   ExecCtx exec_ctx;
60   MemoryAllocator memory_allocator = MemoryAllocator(
61       ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test"));
62   Arena* a = Arena::Create(GetParam().initial_size, &memory_allocator);
63   std::vector<void*> allocated;
64   for (auto alloc : GetParam().allocs) {
65     void* p = a->Alloc(alloc);
66     // ensure the returned address is aligned
67     EXPECT_EQ(((intptr_t)p & 0xf), 0);
68     // ensure no duplicate results
69     for (auto other_p : allocated) {
70       EXPECT_NE(p, other_p);
71     }
72     // ensure writable
73     memset(p, 1, alloc);
74     allocated.push_back(p);
75   }
76   a->Destroy();
77 }
78 
79 INSTANTIATE_TEST_SUITE_P(
80     AllocTests, AllocTest,
81     ::testing::Values(AllocShape{0, {1}}, AllocShape{1, {1}},
82                       AllocShape{1, {2}}, AllocShape{1, {3}},
83                       AllocShape{1, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}},
84                       AllocShape{6, {1, 2, 3}}));
85 
86 #define CONCURRENT_TEST_THREADS 10
87 
concurrent_test_iterations()88 size_t concurrent_test_iterations() {
89   if (sizeof(void*) < 8) return 1000;
90   return 100000;
91 }
92 
93 typedef struct {
94   gpr_event ev_start;
95   Arena* arena;
96 } concurrent_test_args;
97 
98 class ArenaTest : public ::testing::Test {
99  protected:
100   MemoryAllocator memory_allocator_ = MemoryAllocator(
101       ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test"));
102 };
103 
TEST_F(ArenaTest,NoOp)104 TEST_F(ArenaTest, NoOp) {
105   ExecCtx exec_ctx;
106   Arena::Create(1, &memory_allocator_)->Destroy();
107 }
108 
TEST_F(ArenaTest,ManagedNew)109 TEST_F(ArenaTest, ManagedNew) {
110   ExecCtx exec_ctx;
111   Arena* arena = Arena::Create(1, &memory_allocator_);
112   for (int i = 0; i < 100; i++) {
113     arena->ManagedNew<std::unique_ptr<int>>(std::make_unique<int>(i));
114   }
115   arena->Destroy();
116 }
117 
TEST_F(ArenaTest,ConcurrentAlloc)118 TEST_F(ArenaTest, ConcurrentAlloc) {
119   concurrent_test_args args;
120   gpr_event_init(&args.ev_start);
121   args.arena = Arena::Create(1024, &memory_allocator_);
122 
123   Thread thds[CONCURRENT_TEST_THREADS];
124 
125   for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
126     thds[i] = Thread(
127         "grpc_concurrent_test",
128         [](void* arg) {
129           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
130           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
131           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
132             *static_cast<char*>(a->arena->Alloc(1)) = static_cast<char>(i);
133           }
134         },
135         &args);
136     thds[i].Start();
137   }
138 
139   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
140 
141   for (auto& th : thds) {
142     th.Join();
143   }
144 
145   args.arena->Destroy();
146 }
147 
TEST_F(ArenaTest,ConcurrentManagedNew)148 TEST_F(ArenaTest, ConcurrentManagedNew) {
149   concurrent_test_args args;
150   gpr_event_init(&args.ev_start);
151   args.arena = Arena::Create(1024, &memory_allocator_);
152 
153   Thread thds[CONCURRENT_TEST_THREADS];
154 
155   for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
156     thds[i] = Thread(
157         "grpc_concurrent_test",
158         [](void* arg) {
159           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
160           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
161           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
162             a->arena->ManagedNew<std::unique_ptr<int>>(
163                 std::make_unique<int>(static_cast<int>(i)));
164           }
165         },
166         &args);
167     thds[i].Start();
168   }
169 
170   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
171 
172   for (auto& th : thds) {
173     th.Join();
174   }
175 
176   args.arena->Destroy();
177 }
178 
179 template <typename Int>
Scribble(Int * ints,int n,int offset)180 void Scribble(Int* ints, int n, int offset) {
181   for (int i = 0; i < n; i++) {
182     ints[i] = static_cast<Int>(i + offset);
183   }
184 }
185 
186 template <typename Int>
IsScribbled(Int * ints,int n,int offset)187 bool IsScribbled(Int* ints, int n, int offset) {
188   for (int i = 0; i < n; i++) {
189     if (ints[i] != static_cast<Int>(i + offset)) return false;
190   }
191   return true;
192 }
193 
194 #ifndef GRPC_ARENA_POOLED_ALLOCATIONS_USE_MALLOC
TEST_F(ArenaTest,PooledObjectsArePooled)195 TEST_F(ArenaTest, PooledObjectsArePooled) {
196   struct TestObj {
197     char a[100];
198   };
199 
200   auto arena = MakeScopedArena(1024, &memory_allocator_);
201   auto obj = arena->MakePooled<TestObj>();
202   Scribble(obj->a, 100, 1);
203   EXPECT_TRUE(IsScribbled(obj->a, 100, 1));
204   void* p = obj.get();
205   obj.reset();
206   obj = arena->MakePooled<TestObj>();
207   EXPECT_FALSE(IsScribbled(obj->a, 100, 1));
208   EXPECT_EQ(p, obj.get());
209   Scribble(obj->a, 100, 2);
210   EXPECT_TRUE(IsScribbled(obj->a, 100, 2));
211 }
212 #endif
213 
TEST_F(ArenaTest,CreateManyObjects)214 TEST_F(ArenaTest, CreateManyObjects) {
215   struct TestObj {
216     char a[100];
217   };
218   auto arena = MakeScopedArena(1024, &memory_allocator_);
219   std::vector<Arena::PoolPtr<TestObj>> objs;
220   objs.reserve(1000);
221   for (int i = 0; i < 1000; i++) {
222     objs.emplace_back(arena->MakePooled<TestObj>());
223     Scribble(objs.back()->a, 100, i);
224   }
225   for (int i = 0; i < 1000; i++) {
226     EXPECT_TRUE(IsScribbled(objs[i]->a, 100, i));
227   }
228 }
229 
TEST_F(ArenaTest,CreateManyObjectsWithDestructors)230 TEST_F(ArenaTest, CreateManyObjectsWithDestructors) {
231   using TestObj = std::unique_ptr<int>;
232   auto arena = MakeScopedArena(1024, &memory_allocator_);
233   std::vector<Arena::PoolPtr<TestObj>> objs;
234   objs.reserve(1000);
235   for (int i = 0; i < 1000; i++) {
236     objs.emplace_back(arena->MakePooled<TestObj>(new int(i)));
237   }
238 }
239 
TEST_F(ArenaTest,CreatePoolArray)240 TEST_F(ArenaTest, CreatePoolArray) {
241   auto arena = MakeScopedArena(1024, &memory_allocator_);
242   auto p = arena->MakePooledArray<int>(1024);
243 #ifndef GRPC_ARENA_POOLED_ALLOCATIONS_USE_MALLOC
244   EXPECT_FALSE(p.get_deleter().has_freelist());
245 #else
246   EXPECT_TRUE(p.get_deleter().has_freelist());
247 #endif
248   p = arena->MakePooledArray<int>(5);
249   EXPECT_TRUE(p.get_deleter().has_freelist());
250   Scribble(p.get(), 5, 1);
251   EXPECT_TRUE(IsScribbled(p.get(), 5, 1));
252 }
253 
TEST_F(ArenaTest,ConcurrentMakePooled)254 TEST_F(ArenaTest, ConcurrentMakePooled) {
255   concurrent_test_args args;
256   gpr_event_init(&args.ev_start);
257   args.arena = Arena::Create(1024, &memory_allocator_);
258 
259   class BaseClass {
260    public:
261     virtual ~BaseClass() {}
262     virtual int Foo() = 0;
263   };
264 
265   class Type1 : public BaseClass {
266    public:
267     int Foo() override { return 1; }
268   };
269 
270   class Type2 : public BaseClass {
271    public:
272     int Foo() override { return 2; }
273   };
274 
275   Thread thds1[CONCURRENT_TEST_THREADS / 2];
276   Thread thds2[CONCURRENT_TEST_THREADS / 2];
277 
278   for (int i = 0; i < CONCURRENT_TEST_THREADS / 2; i++) {
279     thds1[i] = Thread(
280         "grpc_concurrent_test",
281         [](void* arg) {
282           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
283           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
284           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
285             EXPECT_EQ(a->arena->MakePooled<Type1>()->Foo(), 1);
286           }
287         },
288         &args);
289     thds1[i].Start();
290 
291     thds2[i] = Thread(
292         "grpc_concurrent_test",
293         [](void* arg) {
294           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
295           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
296           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
297             EXPECT_EQ(a->arena->MakePooled<Type2>()->Foo(), 2);
298           }
299         },
300         &args);
301     thds2[i].Start();
302   }
303 
304   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
305 
306   for (auto& th : thds1) {
307     th.Join();
308   }
309   for (auto& th : thds2) {
310     th.Join();
311   }
312 
313   args.arena->Destroy();
314 }
315 
316 }  // namespace grpc_core
317 
main(int argc,char * argv[])318 int main(int argc, char* argv[]) {
319   grpc::testing::TestEnvironment give_me_a_name(&argc, argv);
320   ::testing::InitGoogleTest(&argc, argv);
321   return RUN_ALL_TESTS();
322 }
323