• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <grpc/support/sync.h>
22 #include <grpc/support/time.h>
23 #include <inttypes.h>
24 #include <string.h>
25 
26 #include <algorithm>
27 #include <iosfwd>
28 #include <memory>
29 #include <ostream>
30 #include <string>
31 #include <vector>
32 
33 #include "absl/strings/str_join.h"
34 #include "gmock/gmock.h"
35 #include "gtest/gtest.h"
36 #include "src/core/lib/iomgr/exec_ctx.h"
37 #include "src/core/lib/resource_quota/resource_quota.h"
38 #include "src/core/util/ref_counted_ptr.h"
39 #include "src/core/util/thd.h"
40 #include "test/core/test_util/test_config.h"
41 
42 using testing::Mock;
43 using testing::Return;
44 using testing::StrictMock;
45 
46 namespace grpc_core {
47 
48 struct AllocShape {
49   size_t initial_size;
50   std::vector<size_t> allocs;
51 };
52 
operator <<(std::ostream & out,const AllocShape & shape)53 std::ostream& operator<<(std::ostream& out, const AllocShape& shape) {
54   out << "AllocShape{initial_size=" << shape.initial_size
55       << ", allocs=" << absl::StrJoin(shape.allocs, ",") << "}";
56   return out;
57 }
58 
59 class AllocTest : public ::testing::TestWithParam<AllocShape> {};
60 
TEST_P(AllocTest,Works)61 TEST_P(AllocTest, Works) {
62   ExecCtx exec_ctx;
63   RefCountedPtr<Arena> a =
64       SimpleArenaAllocator(GetParam().initial_size)->MakeArena();
65   std::vector<void*> allocated;
66   for (auto alloc : GetParam().allocs) {
67     void* p = a->Alloc(alloc);
68     // ensure the returned address is aligned
69     EXPECT_EQ(((intptr_t)p & 0xf), 0);
70     // ensure no duplicate results
71     for (auto other_p : allocated) {
72       EXPECT_NE(p, other_p);
73     }
74     // ensure writable
75     memset(p, 1, alloc);
76     allocated.push_back(p);
77   }
78 }
79 
80 INSTANTIATE_TEST_SUITE_P(
81     AllocTests, AllocTest,
82     ::testing::Values(AllocShape{0, {1}}, AllocShape{1, {1}},
83                       AllocShape{1, {2}}, AllocShape{1, {3}},
84                       AllocShape{1, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}},
85                       AllocShape{6, {1, 2, 3}}));
86 
87 class MockMemoryAllocatorImpl
88     : public grpc_event_engine::experimental::internal::MemoryAllocatorImpl {
89  public:
90   MOCK_METHOD(size_t, Reserve, (MemoryRequest));
91   MOCK_METHOD(grpc_slice, MakeSlice, (MemoryRequest));
92   MOCK_METHOD(void, Release, (size_t));
93   MOCK_METHOD(void, Shutdown, ());
94 };
95 
TEST(ArenaTest,InitialReservationCorrect)96 TEST(ArenaTest, InitialReservationCorrect) {
97   auto allocator_impl = std::make_shared<StrictMock<MockMemoryAllocatorImpl>>();
98   auto allocator = SimpleArenaAllocator(1024, MemoryAllocator(allocator_impl));
99   EXPECT_CALL(*allocator_impl, Reserve(MemoryRequest(1024, 1024)))
100       .WillOnce(Return(1024));
101   auto arena = allocator->MakeArena();
102   Mock::VerifyAndClearExpectations(allocator_impl.get());
103   EXPECT_CALL(*allocator_impl, Release(1024));
104   arena.reset();
105   Mock::VerifyAndClearExpectations(allocator_impl.get());
106   EXPECT_CALL(*allocator_impl, Shutdown());
107 }
108 
TEST(ArenaTest,SubsequentReservationCorrect)109 TEST(ArenaTest, SubsequentReservationCorrect) {
110   auto allocator_impl = std::make_shared<StrictMock<MockMemoryAllocatorImpl>>();
111   auto allocator = SimpleArenaAllocator(1024, MemoryAllocator(allocator_impl));
112   EXPECT_CALL(*allocator_impl, Reserve(MemoryRequest(1024, 1024)))
113       .WillOnce(Return(1024));
114   auto arena = allocator->MakeArena();
115   Mock::VerifyAndClearExpectations(allocator_impl.get());
116   EXPECT_CALL(*allocator_impl,
117               Reserve(MemoryRequest(4096 + Arena::ArenaZoneOverhead(),
118                                     4096 + Arena::ArenaZoneOverhead())))
119       .WillOnce(Return(4096 + Arena::ArenaZoneOverhead()));
120   arena->Alloc(4096);
121   Mock::VerifyAndClearExpectations(allocator_impl.get());
122   EXPECT_CALL(*allocator_impl,
123               Release(1024 + 4096 + Arena::ArenaZoneOverhead()));
124   arena.reset();
125   Mock::VerifyAndClearExpectations(allocator_impl.get());
126   EXPECT_CALL(*allocator_impl, Shutdown());
127 }
128 
129 #define CONCURRENT_TEST_THREADS 10
130 
concurrent_test_iterations()131 size_t concurrent_test_iterations() {
132   if (sizeof(void*) < 8) return 1000;
133   return 100000;
134 }
135 
136 typedef struct {
137   gpr_event ev_start;
138   RefCountedPtr<Arena> arena;
139 } concurrent_test_args;
140 
TEST(ArenaTest,NoOp)141 TEST(ArenaTest, NoOp) { SimpleArenaAllocator()->MakeArena(); }
142 
TEST(ArenaTest,ManagedNew)143 TEST(ArenaTest, ManagedNew) {
144   ExecCtx exec_ctx;
145   auto arena = SimpleArenaAllocator(1)->MakeArena();
146   for (int i = 0; i < 100; i++) {
147     arena->ManagedNew<std::unique_ptr<int>>(std::make_unique<int>(i));
148   }
149 }
150 
TEST(ArenaTest,ConcurrentAlloc)151 TEST(ArenaTest, ConcurrentAlloc) {
152   concurrent_test_args args;
153   gpr_event_init(&args.ev_start);
154   args.arena = SimpleArenaAllocator()->MakeArena();
155 
156   Thread thds[CONCURRENT_TEST_THREADS];
157 
158   for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
159     thds[i] = Thread(
160         "grpc_concurrent_test",
161         [](void* arg) {
162           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
163           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
164           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
165             *static_cast<char*>(a->arena->Alloc(1)) = static_cast<char>(i);
166           }
167         },
168         &args);
169     thds[i].Start();
170   }
171 
172   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
173 
174   for (auto& th : thds) {
175     th.Join();
176   }
177 }
178 
TEST(ArenaTest,ConcurrentManagedNew)179 TEST(ArenaTest, ConcurrentManagedNew) {
180   concurrent_test_args args;
181   gpr_event_init(&args.ev_start);
182   args.arena = SimpleArenaAllocator()->MakeArena();
183 
184   Thread thds[CONCURRENT_TEST_THREADS];
185 
186   for (int i = 0; i < CONCURRENT_TEST_THREADS; i++) {
187     thds[i] = Thread(
188         "grpc_concurrent_test",
189         [](void* arg) {
190           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
191           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
192           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
193             a->arena->ManagedNew<std::unique_ptr<int>>(
194                 std::make_unique<int>(static_cast<int>(i)));
195           }
196         },
197         &args);
198     thds[i].Start();
199   }
200 
201   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
202 
203   for (auto& th : thds) {
204     th.Join();
205   }
206 }
207 
208 template <typename Int>
Scribble(Int * ints,int n,int offset)209 void Scribble(Int* ints, int n, int offset) {
210   for (int i = 0; i < n; i++) {
211     ints[i] = static_cast<Int>(i + offset);
212   }
213 }
214 
215 template <typename Int>
IsScribbled(Int * ints,int n,int offset)216 bool IsScribbled(Int* ints, int n, int offset) {
217   for (int i = 0; i < n; i++) {
218     if (ints[i] != static_cast<Int>(i + offset)) return false;
219   }
220   return true;
221 }
222 
TEST(ArenaTest,CreateManyObjects)223 TEST(ArenaTest, CreateManyObjects) {
224   struct TestObj {
225     char a[100];
226   };
227   auto arena = SimpleArenaAllocator()->MakeArena();
228   std::vector<Arena::PoolPtr<TestObj>> objs;
229   objs.reserve(1000);
230   for (int i = 0; i < 1000; i++) {
231     objs.emplace_back(arena->MakePooled<TestObj>());
232     Scribble(objs.back()->a, 100, i);
233   }
234   for (int i = 0; i < 1000; i++) {
235     EXPECT_TRUE(IsScribbled(objs[i]->a, 100, i));
236   }
237 }
238 
TEST(ArenaTest,CreateManyObjectsWithDestructors)239 TEST(ArenaTest, CreateManyObjectsWithDestructors) {
240   using TestObj = std::unique_ptr<int>;
241   auto arena = SimpleArenaAllocator()->MakeArena();
242   std::vector<Arena::PoolPtr<TestObj>> objs;
243   objs.reserve(1000);
244   for (int i = 0; i < 1000; i++) {
245     objs.emplace_back(arena->MakePooled<TestObj>(new int(i)));
246   }
247 }
248 
TEST(ArenaTest,CreatePoolArray)249 TEST(ArenaTest, CreatePoolArray) {
250   auto arena = SimpleArenaAllocator()->MakeArena();
251   auto p = arena->MakePooledArray<int>(1024);
252   EXPECT_TRUE(p.get_deleter().has_freelist());
253   p = arena->MakePooledArray<int>(5);
254   EXPECT_TRUE(p.get_deleter().has_freelist());
255   Scribble(p.get(), 5, 1);
256   EXPECT_TRUE(IsScribbled(p.get(), 5, 1));
257 }
258 
TEST(ArenaTest,ConcurrentMakePooled)259 TEST(ArenaTest, ConcurrentMakePooled) {
260   concurrent_test_args args;
261   gpr_event_init(&args.ev_start);
262   args.arena = SimpleArenaAllocator()->MakeArena();
263 
264   class BaseClass {
265    public:
266     virtual ~BaseClass() {}
267     virtual int Foo() = 0;
268   };
269 
270   class Type1 : public BaseClass {
271    public:
272     int Foo() override { return 1; }
273   };
274 
275   class Type2 : public BaseClass {
276    public:
277     int Foo() override { return 2; }
278   };
279 
280   Thread thds1[CONCURRENT_TEST_THREADS / 2];
281   Thread thds2[CONCURRENT_TEST_THREADS / 2];
282 
283   for (int i = 0; i < CONCURRENT_TEST_THREADS / 2; i++) {
284     thds1[i] = Thread(
285         "grpc_concurrent_test",
286         [](void* arg) {
287           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
288           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
289           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
290             EXPECT_EQ(a->arena->MakePooled<Type1>()->Foo(), 1);
291           }
292         },
293         &args);
294     thds1[i].Start();
295 
296     thds2[i] = Thread(
297         "grpc_concurrent_test",
298         [](void* arg) {
299           concurrent_test_args* a = static_cast<concurrent_test_args*>(arg);
300           gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
301           for (size_t i = 0; i < concurrent_test_iterations(); i++) {
302             EXPECT_EQ(a->arena->MakePooled<Type2>()->Foo(), 2);
303           }
304         },
305         &args);
306     thds2[i].Start();
307   }
308 
309   gpr_event_set(&args.ev_start, reinterpret_cast<void*>(1));
310 
311   for (auto& th : thds1) {
312     th.Join();
313   }
314   for (auto& th : thds2) {
315     th.Join();
316   }
317 }
318 
319 struct Foo {
Foogrpc_core::Foo320   explicit Foo(int x) : p(std::make_unique<int>(x)) {}
321   std::unique_ptr<int> p;
322 };
323 
324 template <>
325 struct ArenaContextType<Foo> {
Destroygrpc_core::ArenaContextType326   static void Destroy(Foo* p) { p->~Foo(); }
327 };
328 
329 struct alignas(16) VeryAligned {};
330 
TEST(ArenaTest,FooContext)331 TEST(ArenaTest, FooContext) {
332   auto arena = SimpleArenaAllocator()->MakeArena();
333   EXPECT_EQ(arena->GetContext<Foo>(), nullptr);
334   arena->SetContext(arena->New<Foo>(42));
335   ASSERT_NE(arena->GetContext<Foo>(), nullptr);
336   EXPECT_EQ(*arena->GetContext<Foo>()->p, 42);
337   arena->New<VeryAligned>();
338   arena->New<VeryAligned>();
339 }
340 
341 class MockArenaFactory : public ArenaFactory {
342  public:
MockArenaFactory()343   MockArenaFactory()
344       : ArenaFactory(
345             ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator(
346                 "test")) {}
347   MOCK_METHOD(RefCountedPtr<Arena>, MakeArena, (), (override));
348   MOCK_METHOD(void, FinalizeArena, (Arena * arena), (override));
349 };
350 
TEST(ArenaTest,FinalizeArenaIsCalled)351 TEST(ArenaTest, FinalizeArenaIsCalled) {
352   auto factory = MakeRefCounted<StrictMock<MockArenaFactory>>();
353   auto arena = Arena::Create(1, factory);
354   EXPECT_CALL(*factory, FinalizeArena(arena.get()));
355   arena.reset();
356 }
357 
TEST(ArenaTest,AccurateBaseByteCount)358 TEST(ArenaTest, AccurateBaseByteCount) {
359   auto factory = MakeRefCounted<StrictMock<MockArenaFactory>>();
360   auto arena = Arena::Create(1, factory);
361   EXPECT_CALL(*factory, FinalizeArena(arena.get())).WillOnce([](Arena* a) {
362     EXPECT_EQ(a->TotalUsedBytes(),
363               Arena::ArenaOverhead() +
364                   GPR_ROUND_UP_TO_ALIGNMENT_SIZE(
365                       arena_detail::BaseArenaContextTraits::ContextSize()));
366   });
367   arena.reset();
368 }
369 
TEST(ArenaTest,AccurateByteCountWithAllocation)370 TEST(ArenaTest, AccurateByteCountWithAllocation) {
371   auto factory = MakeRefCounted<StrictMock<MockArenaFactory>>();
372   auto arena = Arena::Create(1, factory);
373   arena->Alloc(1000);
374   EXPECT_CALL(*factory, FinalizeArena(arena.get())).WillOnce([](Arena* a) {
375     EXPECT_EQ(a->TotalUsedBytes(),
376               Arena::ArenaOverhead() +
377                   GPR_ROUND_UP_TO_ALIGNMENT_SIZE(
378                       arena_detail::BaseArenaContextTraits::ContextSize()) +
379                   GPR_ROUND_UP_TO_ALIGNMENT_SIZE(1000));
380   });
381   arena.reset();
382 }
383 
384 }  // namespace grpc_core
385 
main(int argc,char * argv[])386 int main(int argc, char* argv[]) {
387   grpc::testing::TestEnvironment give_me_a_name(&argc, argv);
388   ::testing::InitGoogleTest(&argc, argv);
389   return RUN_ALL_TESTS();
390 }
391