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