| /* Copyright 2006-2010 Joaquin M Lopez Munoz. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or copy at |
| * http://www.boost.org/LICENSE_1_0.txt) |
| * |
| * See http://www.boost.org/libs/flyweight for library home page. |
| */ |
| |
| #ifndef BOOST_FLYWEIGHT_REFCOUNTED_HPP |
| #define BOOST_FLYWEIGHT_REFCOUNTED_HPP |
| |
| #if defined(_MSC_VER)&&(_MSC_VER>=1200) |
| #pragma once |
| #endif |
| |
| #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ |
| #include <algorithm> |
| #include <boost/detail/atomic_count.hpp> |
| #include <boost/detail/workaround.hpp> |
| #include <boost/flyweight/refcounted_fwd.hpp> |
| #include <boost/flyweight/tracking_tag.hpp> |
| #include <boost/utility/swap.hpp> |
| |
| /* Refcounting tracking policy. |
| * The implementation deserves some explanation; values are equipped with two |
| * reference counts: |
| * - a regular count of active references |
| * - a deleter count |
| * It looks like a value can be erased when the number of references reaches |
| * zero, but this condition alone can lead to data races: |
| * - Thread A detaches the last reference to x and is preempted. |
| * - Thread B looks for x, finds it and attaches a reference to it. |
| * - Thread A resumes and proceeds with erasing x, leaving a dangling |
| * reference in thread B. |
| * Here is where the deleter count comes into play. This count is |
| * incremented when the reference count changes from 0 to 1, and decremented |
| * when a thread is about to check a value for erasure; it can be seen that a |
| * value is effectively erasable only when the deleter count goes down to 0 |
| * (unless there are dangling references due to abnormal program termination, |
| * for instance if std::exit is called). |
| */ |
| |
| namespace boost{ |
| |
| namespace flyweights{ |
| |
| namespace detail{ |
| |
| template<typename Value,typename Key> |
| class refcounted_value |
| { |
| public: |
| explicit refcounted_value(const Value& x_): |
| x(x_),ref(0),del_ref(0) |
| {} |
| |
| refcounted_value(const refcounted_value& r): |
| x(r.x),ref(0),del_ref(0) |
| {} |
| |
| refcounted_value& operator=(const refcounted_value& r) |
| { |
| x=r.x; |
| return *this; |
| } |
| |
| operator const Value&()const{return x;} |
| operator const Key&()const{return x;} |
| |
| #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) |
| private: |
| template<typename,typename> friend class refcounted_handle; |
| #endif |
| |
| long count()const{return ref;} |
| long add_ref()const{return ++ref;} |
| bool release()const{return (--ref==0);} |
| |
| void add_deleter()const{++del_ref;} |
| bool release_deleter()const{return (--del_ref==0);} |
| |
| private: |
| Value x; |
| mutable boost::detail::atomic_count ref; |
| mutable long del_ref; |
| }; |
| |
| template<typename Handle,typename TrackingHelper> |
| class refcounted_handle |
| { |
| public: |
| explicit refcounted_handle(const Handle& h_):h(h_) |
| { |
| if(TrackingHelper::entry(*this).add_ref()==1){ |
| TrackingHelper::entry(*this).add_deleter(); |
| } |
| } |
| |
| refcounted_handle(const refcounted_handle& x):h(x.h) |
| { |
| TrackingHelper::entry(*this).add_ref(); |
| } |
| |
| refcounted_handle& operator=(refcounted_handle x) |
| { |
| swap(*this,x); |
| return *this; |
| } |
| |
| ~refcounted_handle() |
| { |
| if(TrackingHelper::entry(*this).release()){ |
| TrackingHelper::erase(*this,check_erase); |
| } |
| } |
| |
| operator const Handle&()const{return h;} |
| |
| friend void swap(refcounted_handle& x, refcounted_handle& y) |
| { |
| boost::swap(x.h,y.h); |
| } |
| |
| private: |
| static bool check_erase(const refcounted_handle& x) |
| { |
| return TrackingHelper::entry(x).release_deleter(); |
| } |
| |
| Handle h; |
| }; |
| |
| } /* namespace flyweights::detail */ |
| |
| struct refcounted:tracking_marker |
| { |
| struct entry_type |
| { |
| template<typename Value,typename Key> |
| struct apply |
| { |
| typedef detail::refcounted_value<Value,Key> type; |
| }; |
| }; |
| |
| struct handle_type |
| { |
| template<typename Handle,typename TrackingHelper> |
| struct apply |
| { |
| typedef detail::refcounted_handle<Handle,TrackingHelper> type; |
| }; |
| }; |
| }; |
| |
| } /* namespace flyweights */ |
| |
| } /* namespace boost */ |
| |
| #endif |