storage_ptr.hpp 13 KB

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