/proc/self/cwd/common/memory.h
Line | Count | Source |
1 | | // Copyright 2023 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_ |
16 | | #define THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_ |
17 | | |
18 | | #include <cstddef> |
19 | | #include <memory> |
20 | | #include <ostream> |
21 | | #include <type_traits> |
22 | | #include <utility> |
23 | | |
24 | | #include "absl/base/attributes.h" |
25 | | #include "absl/base/nullability.h" |
26 | | #include "absl/log/absl_check.h" |
27 | | #include "absl/numeric/bits.h" |
28 | | #include "common/allocator.h" |
29 | | #include "internal/to_address.h" // IWYU pragma: keep |
30 | | #include "google/protobuf/arena.h" |
31 | | |
32 | | namespace cel { |
33 | | |
34 | | // Obtain the address of the underlying element from a raw pointer or "fancy" |
35 | | // pointer. |
36 | | using internal::to_address; |
37 | | |
38 | | // MemoryManagement is an enumeration of supported memory management forms |
39 | | // underlying `cel::MemoryManager`. |
40 | | enum class MemoryManagement { |
41 | | // Region-based (a.k.a. arena). Memory is allocated in fixed size blocks and |
42 | | // deallocated all at once upon destruction of the `cel::MemoryManager`. |
43 | | kPooling = 1, |
44 | | // Reference counting. Memory is allocated with an associated reference |
45 | | // counter. When the reference counter hits 0, it is deallocated. |
46 | | kReferenceCounting, |
47 | | }; |
48 | | |
49 | | std::ostream& operator<<(std::ostream& out, MemoryManagement memory_management); |
50 | | |
51 | | class MemoryManager; |
52 | | |
53 | | // `ReferenceCountingMemoryManager` is a `MemoryManager` which employs automatic |
54 | | // memory management through reference counting. |
55 | | class ABSL_DEPRECATED("Do not use") ReferenceCountingMemoryManager final { |
56 | | public: |
57 | | ReferenceCountingMemoryManager(const ReferenceCountingMemoryManager&) = |
58 | | delete; |
59 | | ReferenceCountingMemoryManager(ReferenceCountingMemoryManager&&) = delete; |
60 | | ReferenceCountingMemoryManager& operator=( |
61 | | const ReferenceCountingMemoryManager&) = delete; |
62 | | ReferenceCountingMemoryManager& operator=(ReferenceCountingMemoryManager&&) = |
63 | | delete; |
64 | | |
65 | | private: |
66 | | static void* Allocate(size_t size, size_t alignment); |
67 | | |
68 | | static bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept; |
69 | | |
70 | | explicit ReferenceCountingMemoryManager() = default; |
71 | | |
72 | | friend class MemoryManager; |
73 | | }; |
74 | | |
75 | | // `PoolingMemoryManager` is a `MemoryManager` which employs automatic |
76 | | // memory management through memory pooling. |
77 | | class ABSL_DEPRECATED("Do not use") PoolingMemoryManager final { |
78 | | public: |
79 | | PoolingMemoryManager(const PoolingMemoryManager&) = delete; |
80 | | PoolingMemoryManager(PoolingMemoryManager&&) = delete; |
81 | | PoolingMemoryManager& operator=(const PoolingMemoryManager&) = delete; |
82 | | PoolingMemoryManager& operator=(PoolingMemoryManager&&) = delete; |
83 | | |
84 | | private: |
85 | | // Allocates memory directly from the allocator used by this memory manager. |
86 | | // If `memory_management()` returns `MemoryManagement::kReferenceCounting`, |
87 | | // this allocation *must* be explicitly deallocated at some point via |
88 | | // `Deallocate`. Otherwise deallocation is optional. |
89 | | ABSL_MUST_USE_RESULT static void* Allocate(google::protobuf::Arena* absl_nonnull arena, |
90 | 0 | size_t size, size_t alignment) { |
91 | 0 | ABSL_DCHECK(absl::has_single_bit(alignment)) |
92 | 0 | << "alignment must be a power of 2"; |
93 | 0 | if (size == 0) { |
94 | 0 | return nullptr; |
95 | 0 | } |
96 | 0 | return arena->AllocateAligned(size, alignment); |
97 | 0 | } |
98 | | |
99 | | // Attempts to deallocate memory previously allocated via `Allocate`, `size` |
100 | | // and `alignment` must match the values from the previous call to `Allocate`. |
101 | | // Returns `true` if the deallocation was successful and additional calls to |
102 | | // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if |
103 | | // given `nullptr`. |
104 | | static bool Deallocate(google::protobuf::Arena* absl_nonnull, void*, size_t, |
105 | 0 | size_t alignment) noexcept { |
106 | 0 | ABSL_DCHECK(absl::has_single_bit(alignment)) |
107 | 0 | << "alignment must be a power of 2"; |
108 | 0 | return false; |
109 | 0 | } |
110 | | |
111 | | // Registers a custom destructor to be run upon destruction of the memory |
112 | | // management implementation. Return value is always `true`, indicating that |
113 | | // the destructor may be called at some point in the future. |
114 | | static bool OwnCustomDestructor(google::protobuf::Arena* absl_nonnull arena, |
115 | | void* object, |
116 | 0 | void (*absl_nonnull destruct)(void*)) { |
117 | 0 | ABSL_DCHECK(destruct != nullptr); |
118 | 0 | arena->OwnCustomDestructor(object, destruct); |
119 | 0 | return true; |
120 | 0 | } |
121 | | |
122 | | template <typename T> |
123 | | static void DefaultDestructor(void* ptr) { |
124 | | static_assert(!std::is_trivially_destructible_v<T>); |
125 | | static_cast<T*>(ptr)->~T(); |
126 | | } |
127 | | |
128 | | explicit PoolingMemoryManager() = default; |
129 | | |
130 | | friend class MemoryManager; |
131 | | }; |
132 | | |
133 | | // `MemoryManager` is an abstraction for supporting automatic memory management. |
134 | | // All objects created by the `MemoryManager` have a lifetime governed by the |
135 | | // underlying memory management strategy. Currently `MemoryManager` is a |
136 | | // composed type that holds either a reference to |
137 | | // `ReferenceCountingMemoryManager` or owns a `PoolingMemoryManager`. |
138 | | // |
139 | | // ============================ Reference Counting ============================ |
140 | | // `Unique`: The object is valid until destruction of the `Unique`. |
141 | | // |
142 | | // `Shared`: The object is valid so long as one or more `Shared` managing the |
143 | | // object exist. |
144 | | // |
145 | | // ================================= Pooling ================================== |
146 | | // `Unique`: The object is valid until destruction of the underlying memory |
147 | | // resources or of the `Unique`. |
148 | | // |
149 | | // `Shared`: The object is valid until destruction of the underlying memory |
150 | | // resources. |
151 | | class MemoryManager final { |
152 | | public: |
153 | | // Returns a `MemoryManager` which utilizes an arena. |
154 | | ABSL_MUST_USE_RESULT static MemoryManager Pooling( |
155 | 0 | google::protobuf::Arena* absl_nonnull arena) { |
156 | 0 | return MemoryManager(arena); |
157 | 0 | } |
158 | | |
159 | 0 | explicit MemoryManager(Allocator<> allocator) : arena_(allocator.arena()) {} |
160 | | |
161 | | MemoryManager() = delete; |
162 | | MemoryManager(const MemoryManager&) = default; |
163 | | MemoryManager& operator=(const MemoryManager&) = default; |
164 | | |
165 | 0 | MemoryManagement memory_management() const noexcept { |
166 | 0 | return arena_ == nullptr ? MemoryManagement::kReferenceCounting |
167 | 0 | : MemoryManagement::kPooling; |
168 | 0 | } |
169 | | |
170 | | // Allocates memory directly from the allocator used by this memory manager. |
171 | | // If `memory_management()` returns `MemoryManagement::kReferenceCounting`, |
172 | | // this allocation *must* be explicitly deallocated at some point via |
173 | | // `Deallocate`. Otherwise deallocation is optional. |
174 | 0 | ABSL_MUST_USE_RESULT void* Allocate(size_t size, size_t alignment) { |
175 | 0 | if (arena_ == nullptr) { |
176 | 0 | return ReferenceCountingMemoryManager::Allocate(size, alignment); |
177 | 0 | } else { |
178 | 0 | return PoolingMemoryManager::Allocate(arena_, size, alignment); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | // Attempts to deallocate memory previously allocated via `Allocate`, `size` |
183 | | // and `alignment` must match the values from the previous call to `Allocate`. |
184 | | // Returns `true` if the deallocation was successful and additional calls to |
185 | | // `Allocate` may re-use the memory, `false` otherwise. Returns `false` if |
186 | | // given `nullptr`. |
187 | 0 | bool Deallocate(void* ptr, size_t size, size_t alignment) noexcept { |
188 | 0 | if (arena_ == nullptr) { |
189 | 0 | return ReferenceCountingMemoryManager::Deallocate(ptr, size, alignment); |
190 | 0 | } else { |
191 | 0 | return PoolingMemoryManager::Deallocate(arena_, ptr, size, alignment); |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | // Registers a custom destructor to be run upon destruction of the memory |
196 | | // management implementation. A return of `true` indicates the destructor may |
197 | | // be called at some point in the future, `false` if will definitely not be |
198 | | // called. All pooling memory managers return `true` while the reference |
199 | | // counting memory manager returns `false`. |
200 | 0 | bool OwnCustomDestructor(void* object, void (*absl_nonnull destruct)(void*)) { |
201 | 0 | ABSL_DCHECK(destruct != nullptr); |
202 | 0 | if (arena_ == nullptr) { |
203 | 0 | return false; |
204 | 0 | } else { |
205 | 0 | return PoolingMemoryManager::OwnCustomDestructor(arena_, object, |
206 | 0 | destruct); |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | 0 | google::protobuf::Arena* absl_nullable arena() const noexcept { return arena_; } |
211 | | |
212 | | template <typename T> |
213 | | // NOLINTNEXTLINE(google-explicit-constructor) |
214 | | operator Allocator<T>() const { |
215 | | return arena(); |
216 | | } |
217 | | |
218 | 0 | friend void swap(MemoryManager& lhs, MemoryManager& rhs) noexcept { |
219 | 0 | using std::swap; |
220 | 0 | swap(lhs.arena_, rhs.arena_); |
221 | 0 | } |
222 | | |
223 | | private: |
224 | | friend class PoolingMemoryManager; |
225 | | |
226 | 0 | explicit MemoryManager(std::nullptr_t) : arena_(nullptr) {} |
227 | | |
228 | 0 | explicit MemoryManager(google::protobuf::Arena* absl_nonnull arena) : arena_(arena) {} |
229 | | |
230 | | // If `nullptr`, we are using reference counting. Otherwise we are using |
231 | | // Pooling. We use `UnreachablePooling()` as a sentinel to detect use after |
232 | | // move otherwise the moved-from `MemoryManager` would be in a valid state and |
233 | | // utilize reference counting. |
234 | | google::protobuf::Arena* absl_nullable arena_; |
235 | | }; |
236 | | |
237 | | using MemoryManagerRef = MemoryManager; |
238 | | |
239 | | } // namespace cel |
240 | | |
241 | | #endif // THIRD_PARTY_CEL_CPP_COMMON_MEMORY_H_ |