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