storage_ptr.hpp 12 KB

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