refcounted.hpp 4.5 KB

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