refcounted.hpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /* Copyright 2006-2022 Joaquin M Lopez Munoz.
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * See http://www.boost.org/libs/flyweight for library home page.
  7. */
  8. #ifndef BOOST_FLYWEIGHT_REFCOUNTED_HPP
  9. #define BOOST_FLYWEIGHT_REFCOUNTED_HPP
  10. #if defined(_MSC_VER)
  11. #pragma once
  12. #endif
  13. #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
  14. #include <algorithm>
  15. #include <boost/core/invoke_swap.hpp>
  16. #include <boost/detail/atomic_count.hpp>
  17. #include <boost/detail/workaround.hpp>
  18. #include <boost/flyweight/refcounted_fwd.hpp>
  19. #include <boost/flyweight/tracking_tag.hpp>
  20. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  21. #include <utility>
  22. #endif
  23. /* Refcounting tracking policy.
  24. * The implementation deserves some explanation; values are equipped with two
  25. * reference counts:
  26. * - a regular count of active references
  27. * - a deleter count
  28. * It looks like a value can be erased when the number of references reaches
  29. * zero, but this condition alone can lead to data races:
  30. * - Thread A detaches the last reference to x and is preempted.
  31. * - Thread B looks for x, finds it and attaches a reference to it.
  32. * - Thread A resumes and proceeds with erasing x, leaving a dangling
  33. * reference in thread B.
  34. * Here is where the deleter count comes into play. This count is
  35. * incremented when the reference count changes from 0 to 1, and decremented
  36. * when a thread is about to check a value for erasure; it can be seen that a
  37. * value is effectively erasable only when the deleter count goes down to 0
  38. * (unless there are dangling references due to abnormal program termination,
  39. * for instance if std::exit is called).
  40. */
  41. namespace boost{
  42. namespace flyweights{
  43. namespace detail{
  44. template<typename Value,typename Key>
  45. class refcounted_value
  46. {
  47. public:
  48. explicit refcounted_value(const Value& x_):
  49. x(x_),ref(0),del_ref(0)
  50. {}
  51. refcounted_value(const refcounted_value& r):
  52. x(r.x),ref(0),del_ref(0)
  53. {}
  54. refcounted_value& operator=(const refcounted_value& r)
  55. {
  56. x=r.x;
  57. return *this;
  58. }
  59. #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
  60. explicit refcounted_value(Value&& x_):
  61. x(std::move(x_)),ref(0),del_ref(0)
  62. {}
  63. refcounted_value(refcounted_value&& r):
  64. x(std::move(r.x)),ref(0),del_ref(0)
  65. {}
  66. refcounted_value& operator=(refcounted_value&& r)
  67. {
  68. x=std::move(r.x);
  69. return *this;
  70. }
  71. #endif
  72. operator const Value&()const{return x;}
  73. operator const Key&()const{return x;}
  74. #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
  75. private:
  76. template<typename,typename> friend class refcounted_handle;
  77. #endif
  78. long count()const{return ref;}
  79. long add_ref()const{return ++ref;}
  80. bool release()const{return (--ref==0);}
  81. void add_deleter()const{++del_ref;}
  82. bool release_deleter()const{return (--del_ref==0);}
  83. private:
  84. Value x;
  85. mutable boost::detail::atomic_count ref;
  86. mutable long del_ref;
  87. };
  88. template<typename Handle,typename TrackingHelper>
  89. class refcounted_handle
  90. {
  91. public:
  92. explicit refcounted_handle(const Handle& h_):h(h_)
  93. {
  94. if(TrackingHelper::entry(*this).add_ref()==1){
  95. TrackingHelper::entry(*this).add_deleter();
  96. }
  97. }
  98. refcounted_handle(const refcounted_handle& x):h(x.h)
  99. {
  100. TrackingHelper::entry(*this).add_ref();
  101. }
  102. refcounted_handle& operator=(refcounted_handle x)
  103. {
  104. this->swap(x);
  105. return *this;
  106. }
  107. ~refcounted_handle()
  108. {
  109. if(TrackingHelper::entry(*this).release()){
  110. TrackingHelper::erase(*this,check_erase);
  111. }
  112. }
  113. operator const Handle&()const{return h;}
  114. void swap(refcounted_handle& x)
  115. {
  116. boost::core::invoke_swap(h,x.h);
  117. }
  118. private:
  119. static bool check_erase(const refcounted_handle& x)
  120. {
  121. return TrackingHelper::entry(x).release_deleter();
  122. }
  123. Handle h;
  124. };
  125. template<typename Handle,typename TrackingHelper>
  126. void swap(
  127. refcounted_handle<Handle,TrackingHelper>& x,
  128. refcounted_handle<Handle,TrackingHelper>& y)
  129. {
  130. x.swap(y);
  131. }
  132. } /* namespace flyweights::detail */
  133. #if BOOST_WORKAROUND(BOOST_MSVC,<=1500)
  134. /* swap lookup by boost::core::invoke_swap fails under obscure circumstances */
  135. } /* namespace flyweights */
  136. template<typename Handle,typename TrackingHelper>
  137. void swap(
  138. ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& x,
  139. ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& y)
  140. {
  141. ::boost::flyweights::detail::swap(x,y);
  142. }
  143. namespace flyweights{
  144. #endif
  145. struct refcounted:tracking_marker
  146. {
  147. struct entry_type
  148. {
  149. template<typename Value,typename Key>
  150. struct apply
  151. {
  152. typedef detail::refcounted_value<Value,Key> type;
  153. };
  154. };
  155. struct handle_type
  156. {
  157. template<typename Handle,typename TrackingHelper>
  158. struct apply
  159. {
  160. typedef detail::refcounted_handle<Handle,TrackingHelper> type;
  161. };
  162. };
  163. };
  164. } /* namespace flyweights */
  165. } /* namespace boost */
  166. #endif