]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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: | |
57 | explicit refcounted_value(const Value& x_): | |
58 | x(x_),ref(0),del_ref(0) | |
59 | {} | |
60 | ||
61 | refcounted_value(const refcounted_value& r): | |
62 | x(r.x),ref(0),del_ref(0) | |
63 | {} | |
64 | ||
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) | |
72 | explicit refcounted_value(Value&& x_): | |
73 | x(std::move(x_)),ref(0),del_ref(0) | |
74 | {} | |
75 | ||
76 | refcounted_value(refcounted_value&& r): | |
77 | x(std::move(r.x)),ref(0),del_ref(0) | |
78 | {} | |
79 | ||
80 | refcounted_value& operator=(refcounted_value&& r) | |
81 | { | |
82 | x=std::move(r.x); | |
83 | return *this; | |
84 | } | |
85 | #endif | |
86 | ||
87 | operator const Value&()const{return x;} | |
88 | 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 | ||
95 | long count()const{return ref;} | |
96 | long add_ref()const{return ++ref;} | |
97 | bool release()const{return (--ref==0);} | |
98 | ||
99 | void add_deleter()const{++del_ref;} | |
100 | 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: | |
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 | ||
119 | refcounted_handle(const refcounted_handle& x):h(x.h) | |
120 | { | |
121 | TrackingHelper::entry(*this).add_ref(); | |
122 | } | |
123 | ||
124 | refcounted_handle& operator=(refcounted_handle x) | |
125 | { | |
126 | this->swap(x); | |
127 | return *this; | |
128 | } | |
129 | ||
130 | ~refcounted_handle() | |
131 | { | |
132 | if(TrackingHelper::entry(*this).release()){ | |
133 | TrackingHelper::erase(*this,check_erase); | |
134 | } | |
135 | } | |
136 | ||
137 | operator const Handle&()const{return h;} | |
138 | ||
139 | void swap(refcounted_handle& x) | |
140 | { | |
141 | std::swap(h,x.h); | |
142 | } | |
143 | ||
144 | private: | |
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> | |
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> | |
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 |