storage_ptr.hpp

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