TLA 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/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 HIT 302 : get_shared() const noexcept
90 : {
91 302 : auto const i = reinterpret_cast<std::uintptr_t>(p_) & ~3;
92 : return static_cast<shared_resource*>(
93 302 : reinterpret_cast<container::pmr::memory_resource*>(i));
94 : }
95 :
96 : void
97 6353135 : addref() const noexcept
98 : {
99 6353135 : if(is_shared())
100 141 : get_shared()->refs.fetch_add(
101 : 1, std::memory_order_relaxed);
102 6353135 : }
103 :
104 : void
105 43991177 : release() const noexcept
106 : {
107 43991177 : if(is_shared())
108 : {
109 161 : auto const p = get_shared();
110 161 : if(p->refs.fetch_sub(1,
111 161 : std::memory_order_acq_rel) == 1)
112 20 : delete p;
113 : }
114 43991177 : }
115 :
116 : template<class T>
117 20 : storage_ptr(
118 : detail::shared_resource_impl<T>* p) noexcept
119 20 : : p_(reinterpret_cast<void*>(
120 : reinterpret_cast<std::uintptr_t>(
121 : static_cast<container::pmr::memory_resource*>(p))
122 20 : + 1 + (json::is_deallocate_trivial<T>::value ? 2 : 0)))
123 : {
124 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 41914527 : ~storage_ptr() noexcept
141 : {
142 41914527 : release();
143 41914527 : }
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 14653481 : storage_ptr() noexcept
188 14653481 : : p_(nullptr)
189 : {
190 14653481 : }
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 57222 : : p_(reinterpret_cast<void*>(
206 : reinterpret_cast<std::uintptr_t>(
207 : static_cast<container::pmr::memory_resource*>(r))
208 17 : + (json::is_deallocate_trivial<T>::value ? 2 : 0)))
209 : {
210 57222 : BOOST_ASSERT(r);
211 57222 : }
212 :
213 : /** Overload
214 :
215 : @tparam V Any type.
216 : @param alloc A @ref boost::container::pmr::polymorphic_allocator to
217 : construct from.
218 : */
219 : template<class V>
220 10 : storage_ptr(
221 : container::pmr::polymorphic_allocator<V> const& alloc) noexcept
222 10 : : p_(alloc.resource())
223 : {
224 10 : }
225 :
226 : /** Overload
227 :
228 : @param other Another pointer.
229 : */
230 22965891 : storage_ptr(
231 : storage_ptr&& other) noexcept
232 22965891 : : p_(detail::exchange(other.p_, nullptr))
233 : {
234 22965891 : }
235 :
236 : /** Overload
237 :
238 : @param other
239 : */
240 6353134 : storage_ptr(
241 : storage_ptr const& other) noexcept
242 6353134 : : p_(other.p_)
243 : {
244 6353134 : addref();
245 6353134 : }
246 : /// @}
247 :
248 : /** Assignment operators.
249 :
250 : This function assigns a pointer that points to the same memory resource
251 : as `other`, with the same ownership:
252 :
253 : @li If `other` is non-owning, then the assigned-to pointer will be be
254 : non-owning.
255 :
256 : @li If `other` has shared ownership, then **(1)** transfers ownership
257 : to the assigned-to pointer, while after **(2)** it shares the ownership
258 : with `other`.
259 :
260 : If the assigned-to pointer previously had shared ownership, it is
261 : released before the function returns.
262 :
263 : After **(1)**, `other` will point to the
264 : \<\<default_memory_resource,default memory resource\>\>.
265 :
266 : @par Complexity
267 : Constant.
268 :
269 : @par Exception Safety
270 : No-throw guarantee.
271 :
272 : @param other Another pointer.
273 :
274 : @{
275 : */
276 : storage_ptr&
277 2076649 : operator=(
278 : storage_ptr&& other) noexcept
279 : {
280 2076649 : release();
281 2076649 : p_ = detail::exchange(other.p_, nullptr);
282 2076649 : return *this;
283 : }
284 :
285 : storage_ptr&
286 1 : operator=(
287 : storage_ptr const& other) noexcept
288 : {
289 1 : other.addref();
290 1 : release();
291 1 : p_ = other.p_;
292 1 : return *this;
293 : }
294 : /// @}
295 :
296 : /** Check if ownership of the memory resource is shared.
297 :
298 : This function returns true for memory resources created using @ref
299 : make_shared_resource.
300 : */
301 : bool
302 50344313 : is_shared() const noexcept
303 : {
304 50344313 : auto const i = reinterpret_cast<std::uintptr_t>(p_);
305 50344313 : return (i & 1) != 0;
306 : }
307 :
308 : /** Check if calling `deallocate` on the memory resource has no effect.
309 :
310 : This function is used to determine if the deallocate function of the
311 : pointed to memory resource is trivial. The value of @ref
312 : is_deallocate_trivial is evaluated and saved when the memory resource
313 : is constructed and the type is known, before the type is erased.
314 : */
315 : bool
316 1 : is_deallocate_trivial() const noexcept
317 : {
318 1 : auto const i = reinterpret_cast<std::uintptr_t>(p_);
319 1 : return (i & 2) != 0;
320 : }
321 :
322 : /** Check if ownership of the memory resource is not shared and deallocate is trivial.
323 :
324 : This function is used to determine if calls to deallocate can
325 : effectively be skipped. Equivalent to `! is_shared() &&
326 : is_deallocate_trivial()`.
327 : */
328 : bool
329 4323579 : is_not_shared_and_deallocate_is_trivial() const noexcept
330 : {
331 4323579 : auto const i = reinterpret_cast<std::uintptr_t>(p_);
332 4323579 : return (i & 3) == 2;
333 : }
334 :
335 : /** Return a pointer to the memory resource.
336 :
337 : This function returns a pointer to the
338 : referenced @ref boost::container::pmr::memory_resource.
339 :
340 : @par Complexity
341 : Constant.
342 :
343 : @par Exception Safety
344 : No-throw guarantee.
345 : */
346 : container::pmr::memory_resource*
347 653431 : get() const noexcept
348 : {
349 653431 : if(p_)
350 : {
351 122583 : auto const i = reinterpret_cast<std::uintptr_t>(p_) & ~3;
352 122583 : return reinterpret_cast<container::pmr::memory_resource*>(i);
353 : }
354 530848 : return default_resource::get();
355 : }
356 :
357 : /** Return a pointer to the memory resource.
358 :
359 : This function returns a pointer to the referenced @ref
360 : boost::container::pmr::memory_resource.
361 :
362 : @par Complexity
363 : Constant.
364 :
365 : @par Exception Safety
366 : No-throw guarantee.
367 : */
368 : container::pmr::memory_resource*
369 649785 : operator->() const noexcept
370 : {
371 649785 : return get();
372 : }
373 :
374 : /** Return a reference to the memory resource.
375 :
376 : This function returns a reference to the pointed-to @ref
377 : boost::container::pmr::memory_resource.
378 :
379 : @par Complexity
380 :
381 : Constant.
382 :
383 : @par Exception Safety
384 :
385 : No-throw guarantee.
386 : */
387 : container::pmr::memory_resource&
388 3614 : operator*() const noexcept
389 : {
390 3614 : return *get();
391 : }
392 :
393 : template<class U, class... Args>
394 : friend
395 : storage_ptr
396 : make_shared_resource(Args&&... args);
397 : };
398 :
399 : #if defined(_MSC_VER)
400 : # pragma warning( push )
401 : # if !defined(__clang__) && _MSC_VER <= 1900
402 : # pragma warning( disable : 4702 )
403 : # endif
404 : #endif
405 :
406 : /** Return a pointer that owns a new, dynamically allocated memory resource.
407 :
408 : This function dynamically allocates a new memory resource as if by
409 : `operator new` that uses shared ownership. The lifetime of the memory
410 : resource will be extended until the last @ref storage_ptr which points to
411 : it is destroyed.
412 :
413 : @par Constraints
414 : @code
415 : std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
416 : @endcode
417 :
418 : @par Complexity
419 : Same as `new U( std::forward<Args>(args)... )`.
420 :
421 : @par Exception Safety
422 : Strong guarantee.
423 :
424 : @tparam U The type of memory resource to create.
425 :
426 : @param args Parameters forwarded to the constructor of `U`.
427 : */
428 : template<class U, class... Args>
429 : storage_ptr
430 21 : make_shared_resource(Args&&... args)
431 : {
432 : // If this generates an error, it means that
433 : // `T` is not a memory resource.
434 : BOOST_CORE_STATIC_ASSERT((
435 : std::is_base_of<container::pmr::memory_resource, U>::value));
436 23 : return storage_ptr(new
437 : detail::shared_resource_impl<U>(
438 22 : std::forward<Args>(args)...));
439 : }
440 : #if defined(_MSC_VER)
441 : # pragma warning( pop )
442 : #endif
443 :
444 : /// Overload
445 : inline
446 : bool
447 5 : operator==(
448 : storage_ptr const& lhs,
449 : storage_ptr const& rhs) noexcept
450 : {
451 5 : return lhs.get() == rhs.get();
452 : }
453 :
454 : /// Overload
455 : inline
456 : bool
457 : operator!=(
458 : storage_ptr const& lhs,
459 : storage_ptr const& rhs) noexcept
460 : {
461 : return lhs.get() != rhs.get();
462 : }
463 :
464 : } // namespace json
465 : } // namespace boost
466 :
467 : #endif
|