/* Copyright 2006-2022 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) #pragma once #endif #include /* keep it first to prevent nasty warns in MSVC */ #include #include #include #include #include #include #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #include #endif /* 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 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; } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) explicit refcounted_value(Value&& x_): x(std::move(x_)),ref(0),del_ref(0) {} refcounted_value(refcounted_value&& r): x(std::move(r.x)),ref(0),del_ref(0) {} refcounted_value& operator=(refcounted_value&& r) { x=std::move(r.x); return *this; } #endif 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) { this->swap(x); return *this; } ~refcounted_handle() { if(TrackingHelper::entry(*this).release()){ TrackingHelper::erase(*this,check_erase); } } operator const Handle&()const{return h;} void swap(refcounted_handle& x) { boost::core::invoke_swap(h,x.h); } private: static bool check_erase(const refcounted_handle& x) { return TrackingHelper::entry(x).release_deleter(); } Handle h; }; template void swap( refcounted_handle& x, refcounted_handle& y) { x.swap(y); } } /* namespace flyweights::detail */ #if BOOST_WORKAROUND(BOOST_MSVC,<=1500) /* swap lookup by boost::core::invoke_swap fails under obscure circumstances */ } /* namespace flyweights */ template void swap( ::boost::flyweights::detail::refcounted_handle& x, ::boost::flyweights::detail::refcounted_handle& y) { ::boost::flyweights::detail::swap(x,y); } namespace flyweights{ #endif 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