/* Copyright 2006-2009 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 /* keep it first to prevent nasty warns in MSVC */ #include #include #include #include #include #include #include /* 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. */ namespace boost{ namespace flyweights{ namespace detail{ template 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() { /* count()!=0 most likely indicates that the flyweight factory * has been destructed before some of the flyweight objects using * it. Check for static initialization order problems with this * flyweight type. */ BOOST_ASSERT(count()==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 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 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 struct apply { typedef detail::refcounted_value type; }; }; struct handle_type { template struct apply { typedef detail::refcounted_handle type; }; }; }; } /* namespace flyweights */ } /* namespace boost */ #endif