storage_ptr.hpp 14 KB

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