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