• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gtest/gtest.h"
18 
19 #include <cstddef>
20 #include <thread>
21 
22 #include "berberis/base/mmap.h"
23 #include "berberis/base/mmap_pool.h"
24 #include "berberis/base/page_size.h"
25 
26 namespace berberis {
27 
28 class MmapPoolTest : public ::testing::Test {
29  protected:
30   static constexpr size_t kBlockSize = kMaxPageSize;
31   static constexpr size_t kSizeLimit = 2 * kBlockSize;
32   using Pool = MmapPool<kBlockSize, kSizeLimit>;
33 
34   MmapPoolTest() = default;
35 
ListLength()36   size_t ListLength() {
37     size_t length = 0;
38     Pool::Node* node = Pool::g_nodes_with_available_blocks_.TopForTesting();
39     while (node) {
40       length++;
41       node = node->next;
42     }
43     return length;
44   }
45 
SetUp()46   virtual void SetUp() {
47     // Empty the global pool, possibly populated by other tests.
48     while (ListLength() > 0) {
49       MunmapOrDie(Pool::Alloc(), kBlockSize);
50     }
51   }
52 };
53 
54 namespace {
55 
TEST_F(MmapPoolTest,Smoke)56 TEST_F(MmapPoolTest, Smoke) {
57   char* p1 = static_cast<char*>(Pool::Alloc());
58   ASSERT_TRUE(p1);
59   p1[kBlockSize - 1] = 'a';
60   EXPECT_EQ(ListLength(), 0u);
61 
62   char* p2 = static_cast<char*>(Pool::Alloc());
63   ASSERT_TRUE(p2);
64   p2[kBlockSize - 1] = 'b';
65   EXPECT_EQ(ListLength(), 0u);
66 
67   EXPECT_NE(p1, p2);
68 
69   char* p3 = static_cast<char*>(Pool::Alloc());
70   ASSERT_TRUE(p3);
71   p3[kBlockSize - 1] = 'c';
72   EXPECT_EQ(ListLength(), 0u);
73 
74   Pool::Free(p1);
75   EXPECT_EQ(ListLength(), 1u);
76   p1[kBlockSize - 1] = 'A';
77 
78   Pool::Free(p2);
79   EXPECT_EQ(ListLength(), 2u);
80   p2[kBlockSize - 1] = 'B';
81 
82   Pool::Free(p3);
83   // Length doesn't change!
84   EXPECT_EQ(ListLength(), 2u);
85   // The block is unmapped.
86   EXPECT_DEATH(p3[kBlockSize - 1] = 'C', "");
87 }
88 
TEST_F(MmapPoolTest,Stress)89 TEST_F(MmapPoolTest, Stress) {
90   constexpr size_t kNumThreads = 4;
91   std::thread threads[kNumThreads];
92 
93   for (size_t i = 0; i < kNumThreads; i++) {
94     threads[i] = std::thread([]() {
95       for (int c = 0; c < 1024 * 1024; c++) {
96         char* block = static_cast<char*>(Pool::Alloc());
97         block[kBlockSize - 1] = 'a';
98         Pool::Free(block);
99         // ListLength isn't thread-safe, so we cannot use it here. So we just expect that
100         // Alloc/Free cycles do not result in a crash.
101       }
102     });
103   }
104 
105   for (auto& thread : threads) {
106     thread.join();
107   }
108   EXPECT_EQ(ListLength(), 2u);
109 }
110 
111 }  // namespace
112 
113 }  // namespace berberis
114