123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- /* 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 <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
- #include <algorithm>
- #include <boost/core/invoke_swap.hpp>
- #include <boost/detail/atomic_count.hpp>
- #include <boost/detail/workaround.hpp>
- #include <boost/flyweight/refcounted_fwd.hpp>
- #include <boost/flyweight/tracking_tag.hpp>
- #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
- #include <utility>
- #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<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;
- }
- #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<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)
- {
- 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<typename Handle,typename TrackingHelper>
- void swap(
- refcounted_handle<Handle,TrackingHelper>& x,
- refcounted_handle<Handle,TrackingHelper>& 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<typename Handle,typename TrackingHelper>
- void swap(
- ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& x,
- ::boost::flyweights::detail::refcounted_handle<Handle,TrackingHelper>& y)
- {
- ::boost::flyweights::detail::swap(x,y);
- }
- namespace flyweights{
- #endif
- 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
|