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