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 : std::uintptr_t i_;
87 :
88 : shared_resource*
89 302 : get_shared() const noexcept
90 : {
91 : return static_cast<shared_resource*>(
92 : reinterpret_cast<container::pmr::memory_resource*>(
93 302 : i_ & ~3));
94 : }
95 :
96 : void
97 6353124 : addref() const noexcept
98 : {
99 6353124 : if(is_shared())
100 141 : get_shared()->refs.fetch_add(
101 : 1, std::memory_order_relaxed);
102 6353124 : }
103 :
104 : void
105 43991884 : release() const noexcept
106 : {
107 43991884 : 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 43991884 : }
115 :
116 : template<class T>
117 20 : storage_ptr(
118 : detail::shared_resource_impl<T>* p) noexcept
119 20 : : i_(reinterpret_cast<std::uintptr_t>(
120 20 : static_cast<container::pmr::memory_resource*>(p)) + 1 +
121 : (json::is_deallocate_trivial<T>::value ? 2 : 0))
122 : {
123 20 : BOOST_ASSERT(p);
124 20 : }
125 :
126 : public:
127 : /** Destructor.
128 :
129 : If the pointer has shared ownership of the resource, the shared
130 : ownership is released. If this is the last owned copy, the memory
131 : resource is destroyed.
132 :
133 : @par Complexity
134 : Constant.
135 :
136 : @par Exception Safety
137 : No-throw guarantee.
138 : */
139 41915221 : ~storage_ptr() noexcept
140 : {
141 41915221 : release();
142 41915221 : }
143 :
144 : /** Constructors.
145 :
146 : @li **(1)** constructs a non-owning pointer that refers to the
147 : \<\<default_memory_resource,default memory resource\>\>.
148 :
149 : @li **(2)** constructs a non-owning pointer that points to the memory
150 : resource `r`.
151 :
152 : @li **(3)** constructs a non-owning pointer that points to the same
153 : memory resource as `alloc`, obtained by calling `alloc.resource()`.
154 :
155 : @li **(4)**, **(5)** construct a pointer to the same memory resource as
156 : `other`, with the same ownership.
157 :
158 : After **(4)** and **(5)** if `other` was owning, then the constructed
159 : pointer is also owning. In particular, **(4)** transfers ownership to
160 : the constructed pointer while **(5)** causes it to share ownership with
161 : `other`. Otherwise, and with other overloads the constructed pointer
162 : doesn't own its memory resource and the caller is responsible for
163 : maintaining the lifetime of the pointed-to
164 : @ref boost::container::pmr::memory_resource.
165 :
166 : After **(4)**, `other` will point to the default memory resource.
167 :
168 : @par Constraints
169 : @code
170 : std::is_convertible< T*, boost::container::pmr::memory_resource* >::value == true
171 : @endcode
172 :
173 : @pre
174 : @code
175 : r != nullptr
176 : @endcode
177 :
178 : @par Complexity
179 : Constant.
180 :
181 : @par Exception Safety
182 : No-throw guarantee.
183 :
184 : @{
185 : */
186 14653628 : storage_ptr() noexcept
187 14653628 : : i_(0)
188 : {
189 14653628 : }
190 :
191 : /** Overload
192 :
193 : @tparam T The type of memory resource.
194 : @param r A non-null pointer to the memory resource to use.
195 : */
196 : template<class T
197 : #ifndef BOOST_JSON_DOCS
198 : , class = typename std::enable_if<
199 : std::is_convertible<T*,
200 : container::pmr::memory_resource*>::value>::type
201 : #endif
202 : >
203 57222 : storage_ptr(T* r) noexcept
204 57222 : : i_(reinterpret_cast<std::uintptr_t>(
205 18 : static_cast<container::pmr::memory_resource *>(r)) +
206 : (json::is_deallocate_trivial<T>::value ? 2 : 0))
207 : {
208 57222 : BOOST_ASSERT(r);
209 57222 : }
210 :
211 : /** Overload
212 :
213 : @tparam V Any type.
214 : @param alloc A @ref boost::container::pmr::polymorphic_allocator to
215 : construct from.
216 : */
217 : template<class V>
218 10 : storage_ptr(
219 : container::pmr::polymorphic_allocator<V> const& alloc) noexcept
220 10 : : i_(reinterpret_cast<std::uintptr_t>(
221 10 : alloc.resource()))
222 : {
223 10 : }
224 :
225 : /** Overload
226 :
227 : @param other Another pointer.
228 : */
229 22966567 : storage_ptr(
230 : storage_ptr&& other) noexcept
231 22966567 : : i_(detail::exchange(other.i_, 0))
232 : {
233 22966567 : }
234 :
235 : /** Overload
236 :
237 : @param other
238 : */
239 6353123 : storage_ptr(
240 : storage_ptr const& other) noexcept
241 6353123 : : i_(other.i_)
242 : {
243 6353123 : addref();
244 6353123 : }
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 2076662 : operator=(
277 : storage_ptr&& other) noexcept
278 : {
279 2076662 : release();
280 2076662 : i_ = detail::exchange(other.i_, 0);
281 2076662 : 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 : i_ = other.i_;
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 50345009 : is_shared() const noexcept
302 : {
303 50345009 : return (i_ & 1) != 0;
304 : }
305 :
306 : /** Check if calling `deallocate` on the memory resource has no effect.
307 :
308 : This function is used to determine if the deallocate function of the
309 : pointed to memory resource is trivial. The value of @ref
310 : is_deallocate_trivial is evaluated and saved when the memory resource
311 : is constructed and the type is known, before the type is erased.
312 : */
313 : bool
314 1 : is_deallocate_trivial() const noexcept
315 : {
316 1 : return (i_ & 2) != 0;
317 : }
318 :
319 : /** Check if ownership of the memory resource is not shared and deallocate is trivial.
320 :
321 : This function is used to determine if calls to deallocate can
322 : effectively be skipped. Equivalent to `! is_shared() &&
323 : is_deallocate_trivial()`.
324 : */
325 : bool
326 4324295 : is_not_shared_and_deallocate_is_trivial() const noexcept
327 : {
328 4324295 : return (i_ & 3) == 2;
329 : }
330 :
331 : /** Return a pointer to the memory resource.
332 :
333 : This function returns a pointer to the
334 : referenced @ref boost::container::pmr::memory_resource.
335 :
336 : @par Complexity
337 : Constant.
338 :
339 : @par Exception Safety
340 : No-throw guarantee.
341 : */
342 : container::pmr::memory_resource*
343 654203 : get() const noexcept
344 : {
345 654203 : if(i_ != 0)
346 : return reinterpret_cast<
347 122565 : container::pmr::memory_resource*>(i_ & ~3);
348 531638 : return default_resource::get();
349 : }
350 :
351 : /** Return a pointer to the memory resource.
352 :
353 : This function returns a pointer to the referenced @ref
354 : boost::container::pmr::memory_resource.
355 :
356 : @par Complexity
357 : Constant.
358 :
359 : @par Exception Safety
360 : No-throw guarantee.
361 : */
362 : container::pmr::memory_resource*
363 650715 : operator->() const noexcept
364 : {
365 650715 : return get();
366 : }
367 :
368 : /** Return a reference to the memory resource.
369 :
370 : This function returns a reference to the pointed-to @ref
371 : boost::container::pmr::memory_resource.
372 :
373 : @par Complexity
374 :
375 : Constant.
376 :
377 : @par Exception Safety
378 :
379 : No-throw guarantee.
380 : */
381 : container::pmr::memory_resource&
382 3456 : operator*() const noexcept
383 : {
384 3456 : return *get();
385 : }
386 :
387 : template<class U, class... Args>
388 : friend
389 : storage_ptr
390 : make_shared_resource(Args&&... args);
391 : };
392 :
393 : #if defined(_MSC_VER)
394 : # pragma warning( push )
395 : # if !defined(__clang__) && _MSC_VER <= 1900
396 : # pragma warning( disable : 4702 )
397 : # endif
398 : #endif
399 :
400 : /** Return a pointer that owns a new, dynamically allocated memory resource.
401 :
402 : This function dynamically allocates a new memory resource as if by
403 : `operator new` that uses shared ownership. The lifetime of the memory
404 : resource will be extended until the last @ref storage_ptr which points to
405 : it is destroyed.
406 :
407 : @par Constraints
408 : @code
409 : std::is_base_of< boost::container::pmr::memory_resource, U >::value == true
410 : @endcode
411 :
412 : @par Complexity
413 : Same as `new U( std::forward<Args>(args)... )`.
414 :
415 : @par Exception Safety
416 : Strong guarantee.
417 :
418 : @tparam U The type of memory resource to create.
419 :
420 : @param args Parameters forwarded to the constructor of `U`.
421 : */
422 : template<class U, class... Args>
423 : storage_ptr
424 21 : make_shared_resource(Args&&... args)
425 : {
426 : // If this generates an error, it means that
427 : // `T` is not a memory resource.
428 : BOOST_CORE_STATIC_ASSERT((
429 : std::is_base_of<container::pmr::memory_resource, U>::value));
430 23 : return storage_ptr(new
431 : detail::shared_resource_impl<U>(
432 22 : std::forward<Args>(args)...));
433 : }
434 : #if defined(_MSC_VER)
435 : # pragma warning( pop )
436 : #endif
437 :
438 : /// Overload
439 : inline
440 : bool
441 5 : operator==(
442 : storage_ptr const& lhs,
443 : storage_ptr const& rhs) noexcept
444 : {
445 5 : return lhs.get() == rhs.get();
446 : }
447 :
448 : /// Overload
449 : inline
450 : bool
451 : operator!=(
452 : storage_ptr const& lhs,
453 : storage_ptr const& rhs) noexcept
454 : {
455 : return lhs.get() != rhs.get();
456 : }
457 :
458 : } // namespace json
459 : } // namespace boost
460 :
461 : #endif
|