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 #ifndef BERBERIS_BASE_MMAP_POOL_H_
18 #define BERBERIS_BASE_MMAP_POOL_H_
19 
20 #include <array>
21 #include <cstddef>
22 
23 #include "berberis/base/checks.h"
24 #include "berberis/base/lock_free_stack.h"
25 #include "berberis/base/mmap.h"
26 #include "berberis/base/page_size.h"
27 
28 namespace berberis {
29 
30 // Pool of memory mappings to be used by berberis runtime.
31 // Thread-safe, signal-safe, reentrant.
32 // Singleton interface (no non-static members).
33 template <size_t kBlockSize, size_t kSizeLimit>
34 class MmapPool {
35   static_assert(kBlockSize % kMaxPageSize == 0);
36   static_assert(kBlockSize <= kSizeLimit);
37 
38  public:
Alloc()39   static void* Alloc() {
40     Node* node = g_nodes_with_available_blocks_.Pop();
41     if (!node) {
42       return MmapOrDie(kBlockSize);
43     }
44     // Memorize the block before releasing the node since it may be immediately overwritten.
45     void* block = node->block;
46     g_nodes_without_blocks_.Push(node);
47     return block;
48   }
49 
Free(void * block)50   static void Free(void* block) {
51     Node* node = g_nodes_without_blocks_.Pop();
52     if (!node) {
53       return MunmapOrDie(block, kBlockSize);
54     }
55     node->block = block;
56     g_nodes_with_available_blocks_.Push(node);
57   }
58 
59  private:
60   struct Node {
61     Node* next;
62     void* block;
63   };
64 
65   static_assert(kSizeLimit % kBlockSize == 0);
66   using NodesArray = std::array<Node, kSizeLimit / kBlockSize>;
67 
68   // Helper wrapper to add a constructor from std::array which can be used for
69   // static member initialization.
70   class FreeNodes : public LockFreeStack<Node> {
71    public:
FreeNodes(NodesArray & nodes_arr)72     explicit FreeNodes(NodesArray& nodes_arr) {
73       for (auto& node : nodes_arr) {
74         LockFreeStack<Node>::Push(&node);
75       }
76     }
77   };
78 
79   // Attention: we cannot use blocks as nodes since a thread may unmap block while another thread
80   // still tries to dereference (node->next) it inside LockFreeStack. So instead we use permanent
81   // array of nodes.
82   static NodesArray g_nodes_;
83   static LockFreeStack<Node> g_nodes_with_available_blocks_;
84   static FreeNodes g_nodes_without_blocks_;
85 
86   MmapPool() = delete;
87 
88   friend class MmapPoolTest;
89 };
90 
91 template <size_t kBlockSize, size_t kSizeLimit>
92 std::array<typename MmapPool<kBlockSize, kSizeLimit>::Node, kSizeLimit / kBlockSize>
93     MmapPool<kBlockSize, kSizeLimit>::g_nodes_;
94 
95 template <size_t kBlockSize, size_t kSizeLimit>
96 LockFreeStack<typename MmapPool<kBlockSize, kSizeLimit>::Node>
97     MmapPool<kBlockSize, kSizeLimit>::g_nodes_with_available_blocks_;
98 
99 template <size_t kBlockSize, size_t kSizeLimit>
100 typename MmapPool<kBlockSize, kSizeLimit>::FreeNodes
101     MmapPool<kBlockSize, kSizeLimit>::g_nodes_without_blocks_(
102         MmapPool<kBlockSize, kSizeLimit>::g_nodes_);
103 
104 }  // namespace berberis
105 
106 #endif  // BERBERIS_BASE_MMAP_POOL_H_
107