blob: 9e2343c3fec072e444f867a00f884205a537a6a2 [file] [log] [blame]
/* 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