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