/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 container is used to hold a pointer to a memory resource. The |
28 | | pointed-to resource is always valid. Depending on the means of |
29 | | construction, the ownership will be either: |
30 | | |
31 | | @li Non-owning, when constructing from a raw pointer to |
32 | | `boost::container::pmr::memory_resource` or from a |
33 | | `boost::container::pmr::polymorphic_allocator`. In this case the caller is |
34 | | responsible for ensuring that the lifetime of the memory resource extends |
35 | | until there are no more calls to allocate or deallocate. |
36 | | |
37 | | @li Owning, when constructing using the function |
38 | | @ref make_shared_resource. In this case |
39 | | ownership is shared; the lifetime of the memory |
40 | | resource extends until the last copy of the |
41 | | @ref storage_ptr is destroyed. |
42 | | |
43 | | @par Examples |
44 | | |
45 | | These statements create a memory resource on the |
46 | | stack and construct a pointer from it without |
47 | | taking ownership: |
48 | | @code |
49 | | monotonic_resource mr; // Create our memory resource on the stack |
50 | | storage_ptr sp( &mr ); // Construct a non-owning pointer to the resource |
51 | | @endcode |
52 | | |
53 | | This function creates a pointer to a memory |
54 | | resource using shared ownership and returns it. |
55 | | The lifetime of the memory resource extends until |
56 | | the last copy of the pointer is destroyed: |
57 | | @code |
58 | | // Create a counted memory resource and return it |
59 | | storage_ptr make_storage() |
60 | | { |
61 | | return make_shared_resource< monotonic_resource >(); |
62 | | } |
63 | | @endcode |
64 | | |
65 | | @par Thread Safety |
66 | | |
67 | | Instances of this type provide the default level of |
68 | | thread safety for all C++ objects. Specifically, it |
69 | | conforms to |
70 | | <a href="http://eel.is/c++draft/res.on.data.races"> |
71 | | 16.4.6.10 Data race avoidance</a>. |
72 | | |
73 | | @see |
74 | | @ref make_shared_resource, |
75 | | @ref boost::container::pmr::polymorphic_allocator, |
76 | | @ref boost::container::pmr::memory_resource. |
77 | | |
78 | | */ |
79 | | class storage_ptr |
80 | | { |
81 | | #ifndef BOOST_JSON_DOCS |
82 | | // VFALCO doc toolchain shows this when it shouldn't |
83 | | friend struct detail::shared_resource; |
84 | | #endif |
85 | | using shared_resource = |
86 | | detail::shared_resource; |
87 | | |
88 | | using default_resource = |
89 | | detail::default_resource; |
90 | | |
91 | | std::uintptr_t i_; |
92 | | |
93 | | shared_resource* |
94 | | get_shared() const noexcept |
95 | 0 | { |
96 | 0 | return static_cast<shared_resource*>( |
97 | 0 | reinterpret_cast<container::pmr::memory_resource*>( |
98 | 0 | i_ & ~3)); |
99 | 0 | } |
100 | | |
101 | | void |
102 | | addref() const noexcept |
103 | 14.0M | { |
104 | 14.0M | if(is_shared()) |
105 | 0 | get_shared()->refs.fetch_add( |
106 | 0 | 1, std::memory_order_relaxed); |
107 | 14.0M | } |
108 | | |
109 | | void |
110 | | release() const noexcept |
111 | 37.6M | { |
112 | 37.6M | if(is_shared()) |
113 | 0 | { |
114 | 0 | auto const p = get_shared(); |
115 | 0 | if(p->refs.fetch_sub(1, |
116 | 0 | std::memory_order_acq_rel) == 1) |
117 | 0 | delete p; |
118 | 0 | } |
119 | 37.6M | } |
120 | | |
121 | | template<class T> |
122 | | storage_ptr( |
123 | | detail::shared_resource_impl<T>* p) noexcept |
124 | | : i_(reinterpret_cast<std::uintptr_t>( |
125 | | static_cast<container::pmr::memory_resource*>(p)) + 1 + |
126 | | (json::is_deallocate_trivial<T>::value ? 2 : 0)) |
127 | | { |
128 | | BOOST_ASSERT(p); |
129 | | } |
130 | | |
131 | | public: |
132 | | /** Destructor |
133 | | |
134 | | If the pointer has shared ownership of the |
135 | | resource, the shared ownership is released. |
136 | | If this is the last owned copy, the memory |
137 | | resource is destroyed. |
138 | | |
139 | | @par Complexity |
140 | | Constant. |
141 | | |
142 | | @par Exception Safety |
143 | | No-throw guarantee. |
144 | | */ |
145 | | ~storage_ptr() noexcept |
146 | 37.6M | { |
147 | 37.6M | release(); |
148 | 37.6M | } |
149 | | |
150 | | /** Constructor |
151 | | |
152 | | This constructs a non-owning pointer that refers |
153 | | to the [default memory resource]. |
154 | | |
155 | | @par Complexity |
156 | | Constant. |
157 | | |
158 | | @par Exception Safety |
159 | | No-throw guarantee. |
160 | | |
161 | | [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource |
162 | | */ |
163 | | storage_ptr() noexcept |
164 | | : i_(0) |
165 | | { |
166 | | } |
167 | | |
168 | | /** Constructor |
169 | | |
170 | | This constructs a non-owning pointer that points to the memory resource |
171 | | `r`. The caller is responsible for maintaining the lifetime of the |
172 | | pointed-to `boost::container::pmr::memory_resource`. |
173 | | |
174 | | @par Constraints |
175 | | @code |
176 | | std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true |
177 | | @endcode |
178 | | |
179 | | @par Preconditions |
180 | | @code |
181 | | r != nullptr |
182 | | @endcode |
183 | | |
184 | | @par Exception Safety |
185 | | No-throw guarantee. |
186 | | |
187 | | @param r A pointer to the memory resource to use. |
188 | | This may not be null. |
189 | | */ |
190 | | template<class T |
191 | | #ifndef BOOST_JSON_DOCS |
192 | | , class = typename std::enable_if< |
193 | | std::is_convertible<T*, |
194 | | container::pmr::memory_resource*>::value>::type |
195 | | #endif |
196 | | > |
197 | | storage_ptr(T* r) noexcept |
198 | | : i_(reinterpret_cast<std::uintptr_t>( |
199 | | static_cast<container::pmr::memory_resource *>(r)) + |
200 | | (json::is_deallocate_trivial<T>::value ? 2 : 0)) |
201 | | { |
202 | | BOOST_ASSERT(r); |
203 | | } |
204 | | |
205 | | /** Constructor |
206 | | |
207 | | This constructs a non-owning pointer that points to the same memory |
208 | | resource as `alloc`, obtained by calling `alloc.resource()`. The caller |
209 | | is responsible for maintaining the lifetime of the |
210 | | pointed-to `boost::container::pmr::memory_resource`. |
211 | | |
212 | | @par Constraints |
213 | | @code |
214 | | std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true |
215 | | @endcode |
216 | | |
217 | | @par Exception Safety |
218 | | No-throw guarantee. |
219 | | |
220 | | @param alloc A `boost::container::pmr::polymorphic_allocator` to |
221 | | construct from. |
222 | | */ |
223 | | template<class T> |
224 | | storage_ptr( |
225 | | container::pmr::polymorphic_allocator<T> const& alloc) noexcept |
226 | | : i_(reinterpret_cast<std::uintptr_t>( |
227 | | alloc.resource())) |
228 | | { |
229 | | } |
230 | | |
231 | | /** Move constructor |
232 | | |
233 | | This function constructs a pointer that |
234 | | points to the same memory resource as `other`, |
235 | | with the same ownership: |
236 | | |
237 | | @li If `other` is non-owning, then `*this` |
238 | | will be be non-owning. |
239 | | |
240 | | @li If `other` has shared ownership, then |
241 | | ownership will be transferred to `*this`. |
242 | | |
243 | | After construction, `other` will point |
244 | | to the [default memory resource]. |
245 | | |
246 | | @par Complexity |
247 | | Constant. |
248 | | |
249 | | @par Exception Safety |
250 | | No-throw guarantee. |
251 | | |
252 | | @param other The pointer to construct from. |
253 | | |
254 | | [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource |
255 | | */ |
256 | | storage_ptr( |
257 | | storage_ptr&& other) noexcept |
258 | 29.3M | : i_(detail::exchange(other.i_, 0)) |
259 | 29.3M | { |
260 | 29.3M | } |
261 | | |
262 | | /** Copy constructor |
263 | | |
264 | | This function constructs a pointer that |
265 | | points to the same memory resource as `other`, |
266 | | with the same ownership: |
267 | | |
268 | | @li If `other` is non-owning, then `*this` |
269 | | will be be non-owning. |
270 | | |
271 | | @li If `other` has shared ownership, then |
272 | | `*this` will acquire shared ownership. |
273 | | |
274 | | @par Complexity |
275 | | Constant. |
276 | | |
277 | | @par Exception Safety |
278 | | No-throw guarantee. |
279 | | |
280 | | @param other The pointer to construct from. |
281 | | */ |
282 | | storage_ptr( |
283 | | storage_ptr const& other) noexcept |
284 | 14.0M | : i_(other.i_) |
285 | 14.0M | { |
286 | 14.0M | addref(); |
287 | 14.0M | } |
288 | | |
289 | | /** Move assignment |
290 | | |
291 | | This function assigns a pointer that |
292 | | points to the same memory resource as `other`, |
293 | | with the same ownership: |
294 | | |
295 | | @li If `other` is non-owning, then `*this` |
296 | | will be be non-owning. |
297 | | |
298 | | @li If `other` has shared ownership, then |
299 | | ownership will be transferred to `*this`. |
300 | | |
301 | | After assignment, `other` will point |
302 | | to the [default memory resource]. |
303 | | If `*this` previously had shared ownership, |
304 | | it is released before the function returns. |
305 | | |
306 | | @par Complexity |
307 | | Constant. |
308 | | |
309 | | @par Exception Safety |
310 | | No-throw guarantee. |
311 | | |
312 | | @param other The storage pointer to move. |
313 | | |
314 | | [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource |
315 | | */ |
316 | | storage_ptr& |
317 | | operator=( |
318 | | storage_ptr&& other) noexcept |
319 | 9.48k | { |
320 | 9.48k | release(); |
321 | 9.48k | i_ = detail::exchange(other.i_, 0); |
322 | 9.48k | return *this; |
323 | 9.48k | } |
324 | | |
325 | | /** Copy assignment. |
326 | | |
327 | | This function assigns a pointer that |
328 | | points to the same memory resource as `other`, |
329 | | with the same ownership: |
330 | | |
331 | | @li If `other` is non-owning, then `*this` |
332 | | will be be non-owning. |
333 | | |
334 | | @li If `other` has shared ownership, then |
335 | | `*this` will acquire shared ownership. |
336 | | |
337 | | If `*this` previously had shared ownership, |
338 | | it is released before the function returns. |
339 | | |
340 | | @par Complexity |
341 | | Constant. |
342 | | |
343 | | @par Exception Safety |
344 | | No-throw guarantee. |
345 | | |
346 | | @param other The storage pointer to copy. |
347 | | */ |
348 | | storage_ptr& |
349 | | operator=( |
350 | | storage_ptr const& other) noexcept |
351 | | { |
352 | | other.addref(); |
353 | | release(); |
354 | | i_ = other.i_; |
355 | | return *this; |
356 | | } |
357 | | |
358 | | /** Return `true` if ownership of the memory resource is shared. |
359 | | |
360 | | This function returns true for memory resources |
361 | | created using @ref make_shared_resource. |
362 | | */ |
363 | | bool |
364 | | is_shared() const noexcept |
365 | 51.6M | { |
366 | 51.6M | return (i_ & 1) != 0; |
367 | 51.6M | } |
368 | | |
369 | | /** Return `true` if calling `deallocate` on the memory resource has no effect. |
370 | | |
371 | | This function is used to determine if the deallocate |
372 | | function of the pointed to memory resource is trivial. |
373 | | The value of @ref is_deallocate_trivial is evaluated |
374 | | and saved when the memory resource is constructed |
375 | | and the type is known, before the type is erased. |
376 | | */ |
377 | | bool |
378 | | is_deallocate_trivial() const noexcept |
379 | | { |
380 | | return (i_ & 2) != 0; |
381 | | } |
382 | | |
383 | | /** Return `true` if ownership of the memory resource is not shared and deallocate is trivial. |
384 | | |
385 | | This function is used to determine if calls to deallocate |
386 | | can effectively be skipped. |
387 | | |
388 | | @par Effects |
389 | | Returns `! this->is_shared() && this->is_deallocate_trivial()` |
390 | | */ |
391 | | bool |
392 | | is_not_shared_and_deallocate_is_trivial() const noexcept |
393 | 1.37M | { |
394 | 1.37M | return (i_ & 3) == 2; |
395 | 1.37M | } |
396 | | |
397 | | /** Return a pointer to the memory resource. |
398 | | |
399 | | This function returns a pointer to the |
400 | | referenced `boost::container::pmr::memory_resource`. |
401 | | |
402 | | @par Complexity |
403 | | Constant. |
404 | | |
405 | | @par Exception Safety |
406 | | No-throw guarantee. |
407 | | */ |
408 | | container::pmr::memory_resource* |
409 | | get() const noexcept |
410 | 3.15M | { |
411 | 3.15M | if(i_ != 0) |
412 | 59.7k | return reinterpret_cast< |
413 | 59.7k | container::pmr::memory_resource*>(i_ & ~3); |
414 | 3.09M | return default_resource::get(); |
415 | 3.15M | } |
416 | | |
417 | | /** Return a pointer to the memory resource. |
418 | | |
419 | | This function returns a pointer to the |
420 | | referenced `boost::container::pmr::memory_resource`. |
421 | | |
422 | | @par Complexity |
423 | | Constant. |
424 | | |
425 | | @par Exception Safety |
426 | | No-throw guarantee. |
427 | | */ |
428 | | container::pmr::memory_resource* |
429 | | operator->() const noexcept |
430 | 3.15M | { |
431 | 3.15M | return get(); |
432 | 3.15M | } |
433 | | |
434 | | /** Return a reference to the memory resource. |
435 | | |
436 | | This function returns a reference to the |
437 | | pointed-to `boost::container::pmr::memory_resource`. |
438 | | |
439 | | @par Complexity |
440 | | |
441 | | Constant. |
442 | | |
443 | | @par Exception Safety |
444 | | |
445 | | No-throw guarantee. |
446 | | */ |
447 | | container::pmr::memory_resource& |
448 | | operator*() const noexcept |
449 | 0 | { |
450 | 0 | return *get(); |
451 | 0 | } |
452 | | |
453 | | template<class U, class... Args> |
454 | | friend |
455 | | storage_ptr |
456 | | make_shared_resource(Args&&... args); |
457 | | }; |
458 | | |
459 | | #if defined(_MSC_VER) |
460 | | # pragma warning( push ) |
461 | | # if !defined(__clang__) && _MSC_VER <= 1900 |
462 | | # pragma warning( disable : 4702 ) |
463 | | # endif |
464 | | #endif |
465 | | /** Return shared ownership of a new, dynamically allocated memory resource. |
466 | | |
467 | | This function dynamically allocates a new memory resource |
468 | | as if by `operator new` that uses shared ownership. The |
469 | | lifetime of the memory resource will be extended until |
470 | | the last @ref storage_ptr which points to it is destroyed. |
471 | | |
472 | | @par Mandates |
473 | | @code |
474 | | std::is_base_of< boost::container::pmr::memory_resource, U >::value == true |
475 | | @endcode |
476 | | |
477 | | @par Complexity |
478 | | Same as `new U( std::forward<Args>(args)... )`. |
479 | | |
480 | | @par Exception Safety |
481 | | Strong guarantee. |
482 | | |
483 | | @tparam U The type of memory resource to create. |
484 | | |
485 | | @param args Parameters forwarded to the constructor of `U`. |
486 | | */ |
487 | | template<class U, class... Args> |
488 | | storage_ptr |
489 | | make_shared_resource(Args&&... args) |
490 | | { |
491 | | // If this generates an error, it means that |
492 | | // `T` is not a memory resource. |
493 | | BOOST_STATIC_ASSERT( |
494 | | std::is_base_of< |
495 | | container::pmr::memory_resource, U>::value); |
496 | | return storage_ptr(new |
497 | | detail::shared_resource_impl<U>( |
498 | | std::forward<Args>(args)...)); |
499 | | } |
500 | | #if defined(_MSC_VER) |
501 | | # pragma warning( pop ) |
502 | | #endif |
503 | | |
504 | | /** Return true if two storage pointers point to the same memory resource. |
505 | | |
506 | | This function returns `true` if the |
507 | | `boost::container::pmr::memory_resource` objects pointed to by `lhs` and |
508 | | `rhs` have the same address. |
509 | | */ |
510 | | inline |
511 | | bool |
512 | | operator==( |
513 | | storage_ptr const& lhs, |
514 | | storage_ptr const& rhs) noexcept |
515 | | { |
516 | | return lhs.get() == rhs.get(); |
517 | | } |
518 | | |
519 | | /** Return true if two storage pointers point to different memory resources. |
520 | | |
521 | | This function returns `true` if the |
522 | | `boost::container::pmr::memory_resource` objects pointed to by `lhs` and |
523 | | `rhs` have different addresses. |
524 | | */ |
525 | | inline |
526 | | bool |
527 | | operator!=( |
528 | | storage_ptr const& lhs, |
529 | | storage_ptr const& rhs) noexcept |
530 | | { |
531 | | return lhs.get() != rhs.get(); |
532 | | } |
533 | | |
534 | | } // namespace json |
535 | | } // namespace boost |
536 | | |
537 | | #endif |