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