Line data Source code
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 302 : get_shared() const noexcept
95 : {
96 : return static_cast<shared_resource*>(
97 : reinterpret_cast<container::pmr::memory_resource*>(
98 302 : i_ & ~3));
99 : }
100 :
101 : void
102 6352922 : addref() const noexcept
103 : {
104 6352922 : if(is_shared())
105 141 : get_shared()->refs.fetch_add(
106 : 1, std::memory_order_relaxed);
107 6352922 : }
108 :
109 : void
110 43991462 : release() const noexcept
111 : {
112 43991462 : if(is_shared())
113 : {
114 161 : auto const p = get_shared();
115 161 : if(p->refs.fetch_sub(1,
116 161 : std::memory_order_acq_rel) == 1)
117 20 : delete p;
118 : }
119 43991462 : }
120 :
121 : template<class T>
122 20 : storage_ptr(
123 : detail::shared_resource_impl<T>* p) noexcept
124 20 : : i_(reinterpret_cast<std::uintptr_t>(
125 20 : static_cast<container::pmr::memory_resource*>(p)) + 1 +
126 : (json::is_deallocate_trivial<T>::value ? 2 : 0))
127 : {
128 20 : BOOST_ASSERT(p);
129 20 : }
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 41914812 : ~storage_ptr() noexcept
146 : {
147 41914812 : release();
148 41914812 : }
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 14653596 : storage_ptr() noexcept
164 14653596 : : i_(0)
165 : {
166 14653596 : }
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 57222 : storage_ptr(T* r) noexcept
198 57222 : : i_(reinterpret_cast<std::uintptr_t>(
199 18 : static_cast<container::pmr::memory_resource *>(r)) +
200 : (json::is_deallocate_trivial<T>::value ? 2 : 0))
201 : {
202 57222 : BOOST_ASSERT(r);
203 57222 : }
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 10 : storage_ptr(
225 : container::pmr::polymorphic_allocator<T> const& alloc) noexcept
226 10 : : i_(reinterpret_cast<std::uintptr_t>(
227 10 : alloc.resource()))
228 : {
229 10 : }
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 22966351 : storage_ptr(
257 : storage_ptr&& other) noexcept
258 22966351 : : i_(detail::exchange(other.i_, 0))
259 : {
260 22966351 : }
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 6352921 : storage_ptr(
283 : storage_ptr const& other) noexcept
284 6352921 : : i_(other.i_)
285 : {
286 6352921 : addref();
287 6352921 : }
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 2076649 : operator=(
318 : storage_ptr&& other) noexcept
319 : {
320 2076649 : release();
321 2076649 : i_ = detail::exchange(other.i_, 0);
322 2076649 : return *this;
323 : }
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 1 : operator=(
350 : storage_ptr const& other) noexcept
351 : {
352 1 : other.addref();
353 1 : release();
354 1 : i_ = other.i_;
355 1 : 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 50344385 : is_shared() const noexcept
365 : {
366 50344385 : return (i_ & 1) != 0;
367 : }
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 1 : is_deallocate_trivial() const noexcept
379 : {
380 1 : 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 4323571 : is_not_shared_and_deallocate_is_trivial() const noexcept
393 : {
394 4323571 : return (i_ & 3) == 2;
395 : }
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 653617 : get() const noexcept
410 : {
411 653617 : if(i_ != 0)
412 : return reinterpret_cast<
413 122565 : container::pmr::memory_resource*>(i_ & ~3);
414 531052 : return default_resource::get();
415 : }
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 649823 : operator->() const noexcept
430 : {
431 649823 : return get();
432 : }
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 3762 : operator*() const noexcept
449 : {
450 3762 : return *get();
451 : }
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 21 : 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 23 : return storage_ptr(new
497 : detail::shared_resource_impl<U>(
498 22 : 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 5 : operator==(
513 : storage_ptr const& lhs,
514 : storage_ptr const& rhs) noexcept
515 : {
516 5 : 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
|