VLink  2.0.0
A high-performance communication middleware
memory_resource.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file memory_resource.h
26  * @brief PMR adapter that lets standard pmr-aware containers allocate through @c vlink::MemoryPool.
27  *
28  * @details
29  * @c vlink::MemoryResource derives from @c std::pmr::memory_resource and forwards each request
30  * onto an owned (or shared) @c vlink::MemoryPool. This bridges the standard polymorphic
31  * allocator machinery with VLink's tiered pool.
32  *
33  * @par PMR vs VLink resources
34  *
35  * | Aspect | @c std::pmr::new_delete_resource | @c vlink::MemoryResource |
36  * | --------------------- | ------------------------------------- | ------------------------------------------- |
37  * | Backing allocator | global @c operator @c new / @c delete | @c vlink::MemoryPool (per-tier free lists) |
38  * | Allocation footprint | unpredictable per-call | bounded per-tier; pooled |
39  * | Concurrent allocs | global allocator scaling | per-tier locking, low contention |
40  * | Bypass mode | not applicable | empty tier list yields pass-through |
41  * | Maintenance | not applicable | @c trim releases idle chunks |
42  *
43  * @par Lifetime
44  * - @c MemoryResource(), @c MemoryResource(int) and @c MemoryResource(const @c Config&) each
45  * heap-allocate a private @c MemoryPool and own it for the lifetime of the resource.
46  * - @c MemoryResource::global_instance returns a process-wide singleton that wraps
47  * @c MemoryPool::global_instance; the resource is not deleted by the destructor.
48  *
49  * @par Example
50  * @code
51  * // Shared process-wide resource.
52  * std::pmr::vector<int> v(&vlink::MemoryResource::global_instance());
53  * v.reserve(1024);
54  *
55  * // Private level-3 pyramid:
56  * vlink::MemoryResource res(3);
57  * std::pmr::polymorphic_allocator<char> alloc(&res);
58  * std::pmr::string s(alloc);
59  *
60  * // Private resource with a custom tier list and full-quota preallocation.
61  * vlink::MemoryPool::Config cfg;
62  * cfg.tiers = {{64, 16}, {1024, 4}};
63  * cfg.prealloc = true;
64  * vlink::MemoryResource custom(cfg);
65  * std::pmr::vector<double> w(&custom);
66  * @endcode
67  *
68  * @note @c do_allocate throws @c std::bad_alloc when the underlying pool returns @c nullptr,
69  * satisfying the pmr contract. Equality is identity over the bound @c MemoryPool object.
70  */
71 
72 #pragma once
73 
74 #include <memory>
75 
76 #if defined(__linux__) && __has_include(<memory_resource>)
77 #include <memory_resource>
78 #if !defined(VLINK_ENABLE_BASE_MEMORY_RESOURCE) && defined(__cpp_lib_memory_resource)
79 #define VLINK_ENABLE_BASE_MEMORY_RESOURCE
80 #endif
81 #endif
82 
83 #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
84 
85 #include <cstddef>
86 
87 #include "./macros.h"
88 #include "./memory_pool.h"
89 
90 namespace vlink {
91 
92 /**
93  * @class MemoryResource
94  * @brief Adapter satisfying @c std::pmr::memory_resource that delegates to a @c vlink::MemoryPool.
95  *
96  * @details
97  * Non-copyable, non-movable -- matching the standard interface. The bound pool may be either
98  * owned by the resource or shared with @c MemoryPool::global_instance, depending on the
99  * constructor that produced the instance.
100  */
101 class VLINK_EXPORT MemoryResource : public std::pmr::memory_resource {
102  public:
103  /**
104  * @brief Deleter type used by @c make_unique to return objects back through the pmr allocator.
105  *
106  * @details
107  * Carries one @c polymorphic_allocator copy so @c sizeof(Deleter<T>) equals @c sizeof(void*).
108  */
109  template <typename T>
110  struct Deleter final {
111  std::pmr::polymorphic_allocator<T> alloc;
112 
113  void operator()(T* p) const noexcept {
114  if VLIKELY (p) {
115  auto allocator = alloc;
116  std::allocator_traits<decltype(allocator)>::destroy(allocator, p);
117  allocator.deallocate(p, 1);
118  }
119  }
120  };
121 
122  /**
123  * @brief Constructs a resource that owns a private bypass-mode pool.
124  *
125  * @details
126  * Equivalent to @c MemoryResource(MemoryPool::Config{}); every allocation goes through
127  * @c ::operator @c new.
128  */
129  MemoryResource();
130 
131  /**
132  * @brief Constructs a resource owning a private pool built from the built-in pyramid @p level.
133  *
134  * @details
135  * Forwards to @c MemoryPool(int, @c bool). In bypass mode @p prealloc is ignored.
136  *
137  * @param level Built-in pyramid level in @c [0, @c 9].
138  * @param prealloc When @c true, fills every tier to its quota on construction. Default: @c false.
139  */
140  explicit MemoryResource(int level, bool prealloc = false);
141 
142  /**
143  * @brief Constructs a resource owning a private pool built from @p config.
144  *
145  * @param config Tier configuration forwarded to the @c MemoryPool constructor.
146  */
147  explicit MemoryResource(const MemoryPool::Config& config);
148 
149  /**
150  * @brief Destructor; releases the owned pool when the resource owns one.
151  *
152  * @details
153  * The destructor does not delete the pool of @c global_instance. The caller must guarantee
154  * no live block obtained from this resource is still outstanding at destruction.
155  */
156  ~MemoryResource() override;
157 
158  /**
159  * @brief Returns the underlying @c MemoryPool used by this resource.
160  *
161  * @return Reference to the bound pool (private or shared depending on the constructor).
162  */
163  [[nodiscard]] MemoryPool& get_memory_pool() noexcept;
164 
165  /**
166  * @brief Trims idle chunks from the underlying pool.
167  *
168  * @details
169  * Forwards to @c MemoryPool::trim on the bound pool.
170  */
171  void trim() noexcept;
172 
173  /**
174  * @brief Returns the process-wide @c MemoryResource that wraps @c MemoryPool::global_instance.
175  *
176  * @details
177  * Lazy singleton. The first call's @p use_env_level value is forwarded to
178  * @c MemoryPool::global_instance and decides the underlying pool's configuration; later calls
179  * ignore the argument and return the same resource.
180  *
181  * @param use_env_level Forwarded to @c MemoryPool::global_instance. Default: @c true.
182  * @return Reference to the shared resource.
183  */
184  static MemoryResource& global_instance(bool use_env_level = true);
185 
186  /**
187  * @brief @c std::allocate_shared backed by @c global_instance.
188  *
189  * @tparam T Object type.
190  * @tparam Args Constructor argument types.
191  * @param args Constructor arguments forwarded into the new object.
192  * @return Shared pointer whose control block and object share one pool allocation.
193  */
194  template <typename T, typename... Args>
195  [[maybe_unused]] static std::shared_ptr<T> make_shared(Args&&... args);
196 
197  /**
198  * @brief Pool-backed analogue of @c std::make_unique.
199  *
200  * @details
201  * Returns @c std::unique_ptr<T, @c Deleter<T>>. Storage is returned to the pool when @c T 's
202  * constructor throws.
203  *
204  * @tparam T Object type.
205  * @tparam Args Constructor argument types.
206  * @param args Constructor arguments forwarded into the new object.
207  * @return Owning pointer that returns memory to the pool on destruction.
208  */
209  template <typename T, typename... Args>
210  [[maybe_unused]] static std::unique_ptr<T, MemoryResource::Deleter<T>> make_unique(Args&&... args);
211 
212  protected:
213  void* do_allocate(size_t bytes, size_t alignment) override;
214 
215  void do_deallocate(void* p, size_t bytes, size_t alignment) override;
216 
217  bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override;
218 
219  private:
220  explicit MemoryResource(MemoryPool& global_pool) noexcept;
221 
222  MemoryPool* pool_{nullptr};
223  bool owns_pool_{false};
224 
225  VLINK_DISALLOW_COPY_AND_ASSIGN(MemoryResource)
226 };
227 
228 ////////////////////////////////////////////////////////////////
229 /// Details
230 ////////////////////////////////////////////////////////////////
231 
232 template <typename T, typename... Args>
233 std::shared_ptr<T> MemoryResource::make_shared(Args&&... args) {
234  std::pmr::polymorphic_allocator<T> alloc(&MemoryResource::global_instance());
235 
236  return std::allocate_shared<T>(alloc, std::forward<Args>(args)...);
237 }
238 
239 template <typename T, typename... Args>
240 std::unique_ptr<T, MemoryResource::Deleter<T>> MemoryResource::make_unique(Args&&... args) {
241  std::pmr::polymorphic_allocator<T> alloc(&MemoryResource::global_instance());
242 
243  T* p = alloc.allocate(1);
244 
245  try {
246  std::allocator_traits<decltype(alloc)>::construct(alloc, p, std::forward<Args>(args)...);
247  } catch (...) {
248  alloc.deallocate(p, 1);
249  throw;
250  }
251 
252  return std::unique_ptr<T, MemoryResource::Deleter<T>>{p, MemoryResource::Deleter<T>{alloc}};
253 }
254 
255 } // namespace vlink
256 
257 #else
258 
259 namespace vlink {
260 
261 /**
262  * @namespace vlink::MemoryResource
263  * @brief Fallback shim when @c <memory_resource> is unavailable.
264  *
265  * @details
266  * Only @c make_shared and @c make_unique are emulated by forwarding to the corresponding
267  * @c std versions. The @c MemoryResource class, the @c Deleter alias and
268  * @c global_instance are not provided in this mode.
269  */
270 namespace MemoryResource { // NOLINT(readability-identifier-naming)
271 
272 /**
273  * @brief Fallback @c make_shared forwarding to @c std::make_shared.
274  */
275 template <typename T, typename... Args>
276 [[maybe_unused]] std::shared_ptr<T> make_shared(Args&&... args) {
277  return std::make_shared<T>(std::forward<Args>(args)...);
278 }
279 
280 /**
281  * @brief Fallback @c make_unique forwarding to @c std::make_unique.
282  */
283 template <typename T, typename... Args>
284 [[maybe_unused]] std::unique_ptr<T> make_unique(Args&&... args) {
285  return std::make_unique<T>(std::forward<Args>(args)...);
286 }
287 
288 } // namespace MemoryResource
289 
290 } // namespace vlink
291 
292 #endif
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174
Size-class tiered memory pool with per-tier free lists and runtime statistics.