/src/boost/boost/json/storage_ptr.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) |
3 | | // |
4 | | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
5 | | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
6 | | // |
7 | | // Official repository: https://github.com/boostorg/json |
8 | | // |
9 | | |
10 | | #ifndef BOOST_JSON_STORAGE_PTR_HPP |
11 | | #define BOOST_JSON_STORAGE_PTR_HPP |
12 | | |
13 | | #include <boost/container/pmr/polymorphic_allocator.hpp> |
14 | | #include <boost/json/detail/config.hpp> |
15 | | #include <boost/json/detail/shared_resource.hpp> |
16 | | #include <boost/json/detail/default_resource.hpp> |
17 | | #include <boost/json/is_deallocate_trivial.hpp> |
18 | | #include <new> |
19 | | #include <type_traits> |
20 | | #include <utility> |
21 | | |
22 | | namespace boost { |
23 | | namespace json { |
24 | | |
25 | | /** A smart pointer to a memory resource. |
26 | | |
27 | | This class is used to hold a pointer to a memory resource. The pointed-to |
28 | | resource is always valid. Depending on the means of construction, the |
29 | | ownership will be either: |
30 | | |
31 | | @li Non-owning, when constructing from a raw pointer to |
32 | | @ref boost::container::pmr::memory_resource or from a |
33 | | @ref boost::container::pmr::polymorphic_allocator. In this case the caller |
34 | | is responsible for ensuring that the lifetime of the memory resource |
35 | | extends until there are no more calls to allocate or deallocate. |
36 | | |
37 | | @li Owning, when constructing using the function @ref make_shared_resource. |
38 | | In this case ownership is shared; the lifetime of the memory resource |
39 | | extends until the last copy of the `storage_ptr` is destroyed. |
40 | | |
41 | | @par Examples |
42 | | These statements create a memory resource on the stack and construct |
43 | | a pointer from it without taking ownership: |
44 | | |
45 | | @code |
46 | | monotonic_resource mr; // Create our memory resource on the stack |
47 | | storage_ptr sp( &mr ); // Construct a non-owning pointer to the resource |
48 | | @endcode |
49 | | |
50 | | This function creates a pointer to a memory resource using shared ownership |
51 | | and returns it. The lifetime of the memory resource extends until the last |
52 | | copy of the pointer is destroyed: |
53 | | |
54 | | @code |
55 | | // Create a counted memory resource and return it |
56 | | storage_ptr make_storage() |
57 | | { |
58 | | return make_shared_resource< monotonic_resource >(); |
59 | | } |
60 | | @endcode |
61 | | |
62 | | @par Thread Safety |
63 | | Instances of this type provide the default level of thread safety for all |
64 | | C++ objects. Specifically, it conforms to |
65 | | [16.4.6.10 Data race avoidance](http://eel.is/c++draft/res.on.data.races). |
66 | | |
67 | | @see |
68 | | @ref make_shared_resource, |
69 | | @ref boost::container::pmr::polymorphic_allocator, |
70 | | @ref boost::container::pmr::memory_resource. |
71 | | |
72 | | */ |
73 | | class storage_ptr |
74 | | { |
75 | | #ifndef BOOST_JSON_DOCS |
76 | | // VFALCO doc toolchain shows this when it shouldn't |
77 | | friend struct detail::shared_resource; |
78 | | #endif |
79 | | using shared_resource = |
80 | | detail::shared_resource; |
81 | | |
82 | | using default_resource = |
83 | | detail::default_resource; |
84 | | |
85 | | std::uintptr_t i_; |
86 | | |
87 | | shared_resource* |
88 | | get_shared() const noexcept |
89 | 0 | { |
90 | 0 | return static_cast<shared_resource*>( |
91 | 0 | reinterpret_cast<container::pmr::memory_resource*>( |
92 | 0 | i_ & ~3)); |
93 | 0 | } |
94 | | |
95 | | void |
96 | | addref() const noexcept |
97 | 14.3M | { |
98 | 14.3M | if(is_shared()) |
99 | 0 | get_shared()->refs.fetch_add( |
100 | 0 | 1, std::memory_order_relaxed); |
101 | 14.3M | } |
102 | | |
103 | | void |
104 | | release() const noexcept |
105 | 39.0M | { |
106 | 39.0M | if(is_shared()) |
107 | 0 | { |
108 | 0 | auto const p = get_shared(); |
109 | 0 | if(p->refs.fetch_sub(1, |
110 | 0 | std::memory_order_acq_rel) == 1) |
111 | 0 | delete p; |
112 | 0 | } |
113 | 39.0M | } |
114 | | |
115 | | template<class T> |
116 | | storage_ptr( |
117 | | detail::shared_resource_impl<T>* p) noexcept |
118 | | : i_(reinterpret_cast<std::uintptr_t>( |
119 | | static_cast<container::pmr::memory_resource*>(p)) + 1 + |
120 | | (json::is_deallocate_trivial<T>::value ? 2 : 0)) |
121 | | { |
122 | | BOOST_ASSERT(p); |
123 | | } |
124 | | |
125 | | public: |
126 | | /** Destructor. |
127 | | |
128 | | If the pointer has shared ownership of the resource, the shared |
129 | | ownership is released. If this is the last owned copy, the memory |
130 | | resource is destroyed. |
131 | | |
132 | | @par Complexity |
133 | | Constant. |
134 | | |
135 | | @par Exception Safety |
136 | | No-throw guarantee. |
137 | | */ |
138 | | ~storage_ptr() noexcept |
139 | 39.0M | { |
140 | 39.0M | release(); |
141 | 39.0M | } |
142 | | |
143 | | /** Constructors. |
144 | | |
145 | | @li **(1)** constructs a non-owning pointer that refers to the |
146 | | \<\<default_memory_resource,default memory resource\>\>. |
147 | | |
148 | | @li **(2)** constructs a non-owning pointer that points to the memory |
149 | | resource `r`. |
150 | | |
151 | | @li **(3)** constructs a non-owning pointer that points to the same |
152 | | memory resource as `alloc`, obtained by calling `alloc.resource()`. |
153 | | |
154 | | @li **(4)**, **(5)** construct a pointer to the same memory resource as |
155 | | `other`, with the same ownership. |
156 | | |
157 | | After **(4)** and **(5)** if `other` was owning, then the constructed |
158 | | pointer is also owning. In particular, **(4)** transfers ownership to |
159 | | the constructed pointer while **(5)** causes it to share ownership with |
160 | | `other`. Otherwise, and with other overloads the constructed pointer |
161 | | doesn't own its memory resource and the caller is responsible for |
162 | | maintaining the lifetime of the pointed-to |
163 | | @ref boost::container::pmr::memory_resource. |
164 | | |
165 | | After **(4)**, `other` will point to the default memory resource. |
166 | | |
167 | | @par Constraints |
168 | | @code |
169 | | std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true |
170 | | @endcode |
171 | | |
172 | | @pre |
173 | | @code |
174 | | r != nullptr |
175 | | @endcode |
176 | | |
177 | | @par Complexity |
178 | | Constant. |
179 | | |
180 | | @par Exception Safety |
181 | | No-throw guarantee. |
182 | | |
183 | | @{ |
184 | | */ |
185 | | storage_ptr() noexcept |
186 | | : i_(0) |
187 | | { |
188 | | } |
189 | | |
190 | | /** Overload |
191 | | |
192 | | @tparam T The type of memory resource. |
193 | | @param r A non-null pointer to the memory resource to use. |
194 | | */ |
195 | | template<class T |
196 | | #ifndef BOOST_JSON_DOCS |
197 | | , class = typename std::enable_if< |
198 | | std::is_convertible<T*, |
199 | | container::pmr::memory_resource*>::value>::type |
200 | | #endif |
201 | | > |
202 | | storage_ptr(T* r) noexcept |
203 | | : i_(reinterpret_cast<std::uintptr_t>( |
204 | | static_cast<container::pmr::memory_resource *>(r)) + |
205 | | (json::is_deallocate_trivial<T>::value ? 2 : 0)) |
206 | | { |
207 | | BOOST_ASSERT(r); |
208 | | } |
209 | | |
210 | | /** Overload |
211 | | |
212 | | @tparam V Any type. |
213 | | @param alloc A @ref boost::container::pmr::polymorphic_allocator to |
214 | | construct from. |
215 | | */ |
216 | | template<class V> |
217 | | storage_ptr( |
218 | | container::pmr::polymorphic_allocator<V> const& alloc) noexcept |
219 | | : i_(reinterpret_cast<std::uintptr_t>( |
220 | | alloc.resource())) |
221 | | { |
222 | | } |
223 | | |
224 | | /** Overload |
225 | | |
226 | | @param other Another pointer. |
227 | | */ |
228 | | storage_ptr( |
229 | | storage_ptr&& other) noexcept |
230 | 29.8M | : i_(detail::exchange(other.i_, 0)) |
231 | 29.8M | { |
232 | 29.8M | } |
233 | | |
234 | | /** Overload |
235 | | |
236 | | @param other |
237 | | */ |
238 | | storage_ptr( |
239 | | storage_ptr const& other) noexcept |
240 | 14.3M | : i_(other.i_) |
241 | 14.3M | { |
242 | 14.3M | addref(); |
243 | 14.3M | } |
244 | | /// @} |
245 | | |
246 | | /** Assignment operators. |
247 | | |
248 | | This function assigns a pointer that points to the same memory resource |
249 | | as `other`, with the same ownership: |
250 | | |
251 | | @li If `other` is non-owning, then the assigned-to pointer will be be |
252 | | non-owning. |
253 | | |
254 | | @li If `other` has shared ownership, then **(1)** transfers ownership |
255 | | to the assigned-to pointer, while after **(2)** it shares the ownership |
256 | | with `other`. |
257 | | |
258 | | If the assigned-to pointer previously had shared ownership, it is |
259 | | released before the function returns. |
260 | | |
261 | | After **(1)**, `other` will point to the |
262 | | \<\<default_memory_resource,default memory resource\>\>. |
263 | | |
264 | | @par Complexity |
265 | | Constant. |
266 | | |
267 | | @par Exception Safety |
268 | | No-throw guarantee. |
269 | | |
270 | | @param other Another pointer. |
271 | | |
272 | | @{ |
273 | | */ |
274 | | storage_ptr& |
275 | | operator=( |
276 | | storage_ptr&& other) noexcept |
277 | 9.45k | { |
278 | 9.45k | release(); |
279 | 9.45k | i_ = detail::exchange(other.i_, 0); |
280 | 9.45k | return *this; |
281 | 9.45k | } |
282 | | |
283 | | storage_ptr& |
284 | | operator=( |
285 | | storage_ptr const& other) noexcept |
286 | | { |
287 | | other.addref(); |
288 | | release(); |
289 | | i_ = other.i_; |
290 | | return *this; |
291 | | } |
292 | | /// @} |
293 | | |
294 | | /** Check if ownership of the memory resource is shared. |
295 | | |
296 | | This function returns true for memory resources created using @ref |
297 | | make_shared_resource. |
298 | | */ |
299 | | bool |
300 | | is_shared() const noexcept |
301 | 53.3M | { |
302 | 53.3M | return (i_ & 1) != 0; |
303 | 53.3M | } |
304 | | |
305 | | /** Check if calling `deallocate` on the memory resource has no effect. |
306 | | |
307 | | This function is used to determine if the deallocate function of the |
308 | | pointed to memory resource is trivial. The value of @ref |
309 | | is_deallocate_trivial is evaluated and saved when the memory resource |
310 | | is constructed and the type is known, before the type is erased. |
311 | | */ |
312 | | bool |
313 | | is_deallocate_trivial() const noexcept |
314 | | { |
315 | | return (i_ & 2) != 0; |
316 | | } |
317 | | |
318 | | /** Check if ownership of the memory resource is not shared and deallocate is trivial. |
319 | | |
320 | | This function is used to determine if calls to deallocate can |
321 | | effectively be skipped. Equivalent to `! is_shared() && |
322 | | is_deallocate_trivial()`. |
323 | | */ |
324 | | bool |
325 | | is_not_shared_and_deallocate_is_trivial() const noexcept |
326 | 1.36M | { |
327 | 1.36M | return (i_ & 3) == 2; |
328 | 1.36M | } |
329 | | |
330 | | /** Return a pointer to the memory resource. |
331 | | |
332 | | This function returns a pointer to the |
333 | | referenced @ref boost::container::pmr::memory_resource. |
334 | | |
335 | | @par Complexity |
336 | | Constant. |
337 | | |
338 | | @par Exception Safety |
339 | | No-throw guarantee. |
340 | | */ |
341 | | container::pmr::memory_resource* |
342 | | get() const noexcept |
343 | 3.10M | { |
344 | 3.10M | if(i_ != 0) |
345 | 41.6k | return reinterpret_cast< |
346 | 41.6k | container::pmr::memory_resource*>(i_ & ~3); |
347 | 3.06M | return default_resource::get(); |
348 | 3.10M | } |
349 | | |
350 | | /** Return a pointer to the memory resource. |
351 | | |
352 | | This function returns a pointer to the referenced @ref |
353 | | boost::container::pmr::memory_resource. |
354 | | |
355 | | @par Complexity |
356 | | Constant. |
357 | | |
358 | | @par Exception Safety |
359 | | No-throw guarantee. |
360 | | */ |
361 | | container::pmr::memory_resource* |
362 | | operator->() const noexcept |
363 | 3.10M | { |
364 | 3.10M | return get(); |
365 | 3.10M | } |
366 | | |
367 | | /** Return a reference to the memory resource. |
368 | | |
369 | | This function returns a reference to the pointed-to @ref |
370 | | boost::container::pmr::memory_resource. |
371 | | |
372 | | @par Complexity |
373 | | |
374 | | Constant. |
375 | | |
376 | | @par Exception Safety |
377 | | |
378 | | No-throw guarantee. |
379 | | */ |
380 | | container::pmr::memory_resource& |
381 | | operator*() const noexcept |
382 | 0 | { |
383 | 0 | return *get(); |
384 | 0 | } |
385 | | |
386 | | template<class U, class... Args> |
387 | | friend |
388 | | storage_ptr |
389 | | make_shared_resource(Args&&... args); |
390 | | }; |
391 | | |
392 | | #if defined(_MSC_VER) |
393 | | # pragma warning( push ) |
394 | | # if !defined(__clang__) && _MSC_VER <= 1900 |
395 | | # pragma warning( disable : 4702 ) |
396 | | # endif |
397 | | #endif |
398 | | |
399 | | /** Return a pointer that owns a new, dynamically allocated memory resource. |
400 | | |
401 | | This function dynamically allocates a new memory resource as if by |
402 | | `operator new` that uses shared ownership. The lifetime of the memory |
403 | | resource will be extended until the last @ref storage_ptr which points to |
404 | | it is destroyed. |
405 | | |
406 | | @par Constraints |
407 | | @code |
408 | | std::is_base_of< boost::container::pmr::memory_resource, U >::value == true |
409 | | @endcode |
410 | | |
411 | | @par Complexity |
412 | | Same as `new U( std::forward<Args>(args)... )`. |
413 | | |
414 | | @par Exception Safety |
415 | | Strong guarantee. |
416 | | |
417 | | @tparam U The type of memory resource to create. |
418 | | |
419 | | @param args Parameters forwarded to the constructor of `U`. |
420 | | */ |
421 | | template<class U, class... Args> |
422 | | storage_ptr |
423 | | make_shared_resource(Args&&... args) |
424 | | { |
425 | | // If this generates an error, it means that |
426 | | // `T` is not a memory resource. |
427 | | BOOST_STATIC_ASSERT( |
428 | | std::is_base_of< |
429 | | container::pmr::memory_resource, U>::value); |
430 | | return storage_ptr(new |
431 | | detail::shared_resource_impl<U>( |
432 | | std::forward<Args>(args)...)); |
433 | | } |
434 | | #if defined(_MSC_VER) |
435 | | # pragma warning( pop ) |
436 | | #endif |
437 | | |
438 | | /// Overload |
439 | | inline |
440 | | bool |
441 | | operator==( |
442 | | storage_ptr const& lhs, |
443 | | storage_ptr const& rhs) noexcept |
444 | | { |
445 | | return lhs.get() == rhs.get(); |
446 | | } |
447 | | |
448 | | /// Overload |
449 | | inline |
450 | | bool |
451 | | operator!=( |
452 | | storage_ptr const& lhs, |
453 | | storage_ptr const& rhs) noexcept |
454 | | { |
455 | | return lhs.get() != rhs.get(); |
456 | | } |
457 | | |
458 | | } // namespace json |
459 | | } // namespace boost |
460 | | |
461 | | #endif |