1 /* Copyright 2006-2014 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 
9 #ifndef BOOST_FLYWEIGHT_REFCOUNTED_HPP
10 #define BOOST_FLYWEIGHT_REFCOUNTED_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
17 #include <algorithm>
18 #include <boost/detail/atomic_count.hpp>
19 #include <boost/detail/workaround.hpp>
20 #include <boost/flyweight/refcounted_fwd.hpp>
21 #include <boost/flyweight/tracking_tag.hpp>
22 #include <boost/utility/swap.hpp>
23 
24 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
25 #include <utility>
26 #endif
27 
28 /* Refcounting tracking policy.
29  * The implementation deserves some explanation; values are equipped with two
30  * reference counts:
31  *   - a regular count of active references
32  *   - a deleter count
33  * It looks like a value can be erased when the number of references reaches
34  * zero, but this condition alone can lead to data races:
35  *   - Thread A detaches the last reference to x and is preempted.
36  *   - Thread B looks for x, finds it and attaches a reference to it.
37  *   - Thread A resumes and proceeds with erasing x, leaving a dangling
38  *     reference in thread B.
39  * Here is where the deleter count comes into play. This count is
40  * incremented when the reference count changes from 0 to 1, and decremented
41  * when a thread is about to check a value for erasure; it can be seen that a
42  * value is effectively erasable only when the deleter count goes down to 0
43  * (unless there are dangling references due to abnormal program termination,
44  * for instance if std::exit is called).
45  */
46 
47 namespace boost{
48 
49 namespace flyweights{
50 
51 namespace detail{
52 
53 template<typename Value,typename Key>
54 class refcounted_value
55 {
56 public:
refcounted_value(const Value & x_)57   explicit refcounted_value(const Value& x_):
58     x(x_),ref(0),del_ref(0)
59   {}
60 
refcounted_value(const refcounted_value & r)61   refcounted_value(const refcounted_value& r):
62     x(r.x),ref(0),del_ref(0)
63   {}
64 
operator =(const refcounted_value & r)65   refcounted_value& operator=(const refcounted_value& r)
66   {
67     x=r.x;
68     return *this;
69   }
70 
71 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
refcounted_value(Value && x_)72   explicit refcounted_value(Value&& x_):
73     x(std::move(x_)),ref(0),del_ref(0)
74   {}
75 
refcounted_value(refcounted_value && r)76   refcounted_value(refcounted_value&& r):
77     x(std::move(r.x)),ref(0),del_ref(0)
78   {}
79 
operator =(refcounted_value && r)80   refcounted_value& operator=(refcounted_value&& r)
81   {
82     x=std::move(r.x);
83     return *this;
84   }
85 #endif
86 
operator const Value&() const87   operator const Value&()const{return x;}
operator const Key&() const88   operator const Key&()const{return x;}
89 
90 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
91 private:
92   template<typename,typename> friend class refcounted_handle;
93 #endif
94 
count() const95   long count()const{return ref;}
add_ref() const96   long add_ref()const{return ++ref;}
release() const97   bool release()const{return (--ref==0);}
98 
add_deleter() const99   void add_deleter()const{++del_ref;}
release_deleter() const100   bool release_deleter()const{return (--del_ref==0);}
101 
102 private:
103   Value                               x;
104   mutable boost::detail::atomic_count ref;
105   mutable long                        del_ref;
106 };
107 
108 template<typename Handle,typename TrackingHelper>
109 class refcounted_handle
110 {
111 public:
refcounted_handle(const Handle & h_)112   explicit refcounted_handle(const Handle& h_):h(h_)
113   {
114     if(TrackingHelper::entry(*this).add_ref()==1){
115       TrackingHelper::entry(*this).add_deleter();
116     }
117   }
118 
refcounted_handle(const refcounted_handle & x)119   refcounted_handle(const refcounted_handle& x):h(x.h)
120   {
121     TrackingHelper::entry(*this).add_ref();
122   }
123 
operator =(refcounted_handle x)124   refcounted_handle& operator=(refcounted_handle x)
125   {
126     this->swap(x);
127     return *this;
128   }
129 
~refcounted_handle()130   ~refcounted_handle()
131   {
132     if(TrackingHelper::entry(*this).release()){
133       TrackingHelper::erase(*this,check_erase);
134     }
135   }
136 
operator const Handle&() const137   operator const Handle&()const{return h;}
138 
swap(refcounted_handle & x)139   void swap(refcounted_handle& x)
140   {
141     std::swap(h,x.h);
142   }
143 
144 private:
check_erase(const refcounted_handle & x)145   static bool check_erase(const refcounted_handle& x)
146   {
147     return TrackingHelper::entry(x).release_deleter();
148   }
149 
150   Handle h;
151 };
152 
153 template<typename Handle,typename TrackingHelper>
swap(refcounted_handle<Handle,TrackingHelper> & x,refcounted_handle<Handle,TrackingHelper> & y)154 void swap(
155   refcounted_handle<Handle,TrackingHelper>& x,
156   refcounted_handle<Handle,TrackingHelper>& y)
157 {
158   x.swap(y);
159 }
160 
161 } /* namespace flyweights::detail */
162 
163 #if BOOST_WORKAROUND(BOOST_MSVC,<=1500)
164 /* swap lookup by boost::swap fails under obscure circumstances */
165 
166 } /* namespace flyweights */
167 
168 template<typename Handle,typename TrackingHelper>
swap(::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper> & x,::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper> & y)169 void swap(
170   ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& x,
171   ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& y)
172 {
173   ::boost::flyweights::detail::swap(x,y);
174 }
175 
176 namespace flyweights{
177 #endif
178 
179 struct refcounted:tracking_marker
180 {
181   struct entry_type
182   {
183     template<typename Value,typename Key>
184     struct apply
185     {
186       typedef detail::refcounted_value<Value,Key> type;
187     };
188   };
189 
190   struct handle_type
191   {
192     template<typename Handle,typename TrackingHelper>
193     struct apply
194     {
195       typedef detail::refcounted_handle<Handle,TrackingHelper> type;
196     };
197   };
198 };
199 
200 } /* namespace flyweights */
201 
202 } /* namespace boost */
203 
204 #endif
205