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 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 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 43989937 : if(is_shared())
109 : {
110 161 : auto const p = get_shared();
111 161 : if(p->refs.fetch_sub(1,
112 161 : std::memory_order_acq_rel) == 1)
113 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 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 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 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 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 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
|