/* 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 |