storage_ptr.hpp

100.0% Lines (74/74) 96.7% Functions (29/30) 73.7% Branches (14/19)
storage_ptr.hpp
Line Branch 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 302 get_shared() const noexcept
90 {
91 302 auto const i = reinterpret_cast<std::uintptr_t>(p_);
92 302 auto const c_ptr = reinterpret_cast<unsigned char*>(p_) - (i & 3);
93 return static_cast<shared_resource*>(
94 302 reinterpret_cast<container::pmr::memory_resource*>(c_ptr));
95 }
96
97 void
98 6352572 addref() const noexcept
99 {
100
2/2
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 6352431 times.
6352572 if(is_shared())
101 141 get_shared()->refs.fetch_add(
102 1, std::memory_order_relaxed);
103 6352572 }
104
105 void
106 43989937 release() const noexcept
107 {
108
2/2
✓ Branch 1 taken 161 times.
✓ Branch 2 taken 43989776 times.
43989937 if(is_shared())
109 {
110 161 auto const p = get_shared();
111 161 if(p->refs.fetch_sub(1,
112
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 141 times.
161 std::memory_order_acq_rel) == 1)
113
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 delete p;
114 }
115 43989937 }
116
117 template<class T>
118 20 storage_ptr(
119 detail::shared_resource_impl<T>* p) noexcept
120 20 : p_(reinterpret_cast<unsigned char*>(
121 static_cast<container::pmr::memory_resource*>(p))
122 20 + 1 + (json::is_deallocate_trivial<T>::value ? 2 : 0))
123 {
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 BOOST_ASSERT(p);
125 20 }
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 41913287 ~storage_ptr() noexcept
141 {
142 41913287 release();
143 41913287 }
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 14653426 storage_ptr() noexcept
188 14653426 : p_(nullptr)
189 {
190 14653426 }
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 57222 storage_ptr(T* r) noexcept
205
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
57222 : p_(reinterpret_cast<unsigned char*>(
206 static_cast<container::pmr::memory_resource*>(r))
207 17 + (json::is_deallocate_trivial<T>::value ? 2 : 0))
208 {
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57222 times.
57222 BOOST_ASSERT(r);
210 57222 }
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 10 storage_ptr(
220 container::pmr::polymorphic_allocator<V> const& alloc) noexcept
221 10 : p_(alloc.resource())
222 {
223 10 }
224
225 /** Overload
226
227 @param other Another pointer.
228 */
229 22965263 storage_ptr(
230 storage_ptr&& other) noexcept
231 22965263 : p_(detail::exchange(other.p_, nullptr))
232 {
233 22965263 }
234
235 /** Overload
236
237 @param other
238 */
239 6352571 storage_ptr(
240 storage_ptr const& other) noexcept
241 6352571 : p_(other.p_)
242 {
243 6352571 addref();
244 6352571 }
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 2076649 operator=(
277 storage_ptr&& other) noexcept
278 {
279 2076649 release();
280 2076649 p_ = detail::exchange(other.p_, nullptr);
281 2076649 return *this;
282 }
283
284 storage_ptr&
285 1 operator=(
286 storage_ptr const& other) noexcept
287 {
288 1 other.addref();
289 1 release();
290 1 p_ = other.p_;
291 1 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 50342510 is_shared() const noexcept
302 {
303 50342510 auto i = reinterpret_cast<std::uintptr_t>(p_);
304 50342510 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 1 is_deallocate_trivial() const noexcept
316 {
317 1 auto i = reinterpret_cast<std::uintptr_t>(p_);
318 1 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 4323548 is_not_shared_and_deallocate_is_trivial() const noexcept
329 {
330 4323548 auto i = reinterpret_cast<std::uintptr_t>(p_);
331 4323548 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 653237 get() const noexcept
347 {
348
2/2
✓ Branch 0 taken 530672 times.
✓ Branch 1 taken 122565 times.
653237 if(!p_)
349 530672 return default_resource::get();
350
351 122565 auto const i = reinterpret_cast<std::uintptr_t>(p_);
352 122565 auto const c_ptr = reinterpret_cast<unsigned char*>(p_) - (i & 3);
353 122565 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 649749 operator->() const noexcept
369 {
370 649749 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 3456 operator*() const noexcept
388 {
389 3456 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 21 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 23 return storage_ptr(new
436 detail::shared_resource_impl<U>(
437
2/3
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
22 std::forward<Args>(args)...));
438 }
439 #if defined(_MSC_VER)
440 # pragma warning( pop )
441 #endif
442
443 /// Overload
444 inline
445 bool
446 5 operator==(
447 storage_ptr const& lhs,
448 storage_ptr const& rhs) noexcept
449 {
450 5 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