Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:25:54

0001 /* Copyright 2024 Joaquin M Lopez Munoz.
0002  * Distributed under the Boost Software License, Version 1.0.
0003  * (See accompanying file LICENSE_1_0.txt or copy at
0004  * http://www.boost.org/LICENSE_1_0.txt)
0005  *
0006  * See http://www.boost.org/libs/flyweight for library home page.
0007  */
0008 
0009 #ifndef BOOST_FLYWEIGHT_CONCURRENT_FACTORY_HPP
0010 #define BOOST_FLYWEIGHT_CONCURRENT_FACTORY_HPP
0011 
0012 #if defined(_MSC_VER)
0013 #pragma once
0014 #endif
0015 
0016 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
0017 #include <atomic>
0018 #include <boost/assert.hpp>
0019 #include <boost/core/allocator_access.hpp> 
0020 #include <boost/core/invoke_swap.hpp>
0021 #include <boost/flyweight/concurrent_factory_fwd.hpp>
0022 #include <boost/flyweight/factory_tag.hpp>
0023 #include <boost/flyweight/detail/is_placeholder_expr.hpp>
0024 #include <boost/unordered/concurrent_node_set.hpp>
0025 #include <chrono>
0026 #include <condition_variable>
0027 #include <mutex>
0028 #include <thread>
0029 #include <type_traits>
0030 #include <utility>
0031 
0032 /* flyweight factory based on boost::concurent_node_set */
0033 
0034 namespace boost{
0035 
0036 namespace flyweights{
0037 
0038 namespace concurrent_factory_detail{
0039 
0040 template<typename Entry>
0041 struct refcounted_entry:Entry
0042 {
0043   explicit refcounted_entry(Entry&& x):Entry{std::move(x)}{}
0044   refcounted_entry(refcounted_entry&& x):
0045     Entry{std::move(static_cast<Entry&>(x))}{}
0046 
0047   long count()const{return ref;}
0048   void add_ref()const{++ref;}
0049   void release()const{--ref;} 
0050   
0051 private:  
0052   mutable std::atomic_long ref{0};
0053 };
0054 
0055 template<typename RefcountedEntry>
0056 class refcounted_handle
0057 {
0058 public:
0059   refcounted_handle(const refcounted_handle& x):p{x.p}{p->add_ref();}
0060   ~refcounted_handle(){p->release();}
0061 
0062   refcounted_handle& operator=(refcounted_handle x)
0063   {
0064     boost::core::invoke_swap(p,x.p);
0065     return *this;
0066   }
0067 
0068   const RefcountedEntry& get()const{return *p;}
0069 
0070 private:
0071   template<typename,typename,typename,typename,typename>
0072   friend class concurrent_factory_class_impl;
0073 
0074   refcounted_handle(const RefcountedEntry* p_):p{p_}
0075   {
0076     /* Doesn't add ref, refcount already incremented by
0077      * concurrent_factory_class_impl before calling this ctor.
0078      */
0079     BOOST_ASSERT(p!=nullptr);
0080     BOOST_ASSERT(p->count()>0);
0081   }
0082 
0083   const RefcountedEntry* p;
0084 };
0085 
0086 template<
0087   typename Entry,typename Key,
0088   typename Hash,typename Pred,typename Allocator
0089 >
0090 class concurrent_factory_class_impl:public factory_marker
0091 {
0092   using entry_type=refcounted_entry<Entry>;
0093   using unrebound_allocator_type=typename std::conditional<
0094     mpl::is_na<Allocator>::value,
0095     std::allocator<entry_type>,
0096     Allocator
0097   >::type;
0098   using container_type=boost::concurrent_node_set<
0099     entry_type,
0100     typename std::conditional<
0101       mpl::is_na<Hash>::value,
0102       boost::hash<Key>,
0103       Hash
0104     >::type,
0105     typename std::conditional<
0106       mpl::is_na<Pred>::value,
0107       std::equal_to<Key>,
0108       Pred
0109     >::type,
0110     boost::allocator_rebind_t<unrebound_allocator_type,entry_type>
0111   >;
0112 
0113 public:
0114   using handle_type=refcounted_handle<entry_type>;
0115   
0116   concurrent_factory_class_impl():gc{[this]{
0117     /* Garbage collector. Traverses the container every gc_time and lockedly
0118      * erases entries without any external reference.
0119      */
0120 
0121     constexpr auto gc_time=std::chrono::seconds(1);
0122 
0123     for(;;){
0124       {
0125         std::unique_lock<std::mutex> lk{m};
0126         if(cv.wait_for(lk,gc_time,[&]{return stop;}))return;
0127       }
0128       cont.erase_if([&](const entry_type& x){return !x.count();});
0129     }
0130   }}
0131   {}
0132 
0133   ~concurrent_factory_class_impl()
0134   {
0135     /* shut the garbage collector down */
0136 
0137     {
0138       std::unique_lock<std::mutex> lk{m};
0139       stop=true;
0140     }
0141     cv.notify_one();
0142     gc.join();
0143   }
0144 
0145   /* Instead of insert, concurrent_factory provides the undocumented extension
0146    * insert_and_visit, accessible through ADL (see global insert_and_visit
0147    * below). This ensures that visitation happens in a locked environment
0148    * even if no external locking is specified.
0149    */
0150 
0151   template<typename F>
0152   handle_type insert_and_visit(Entry&& x,F f)
0153   {
0154     const entry_type* p=nullptr;
0155     auto              g=[&p,&f](const entry_type& x){
0156       f(static_cast<const Entry&>(x));
0157       x.add_ref();
0158       p=std::addressof(x);
0159     };
0160 
0161     cont.insert_and_visit(entry_type{std::move(x)},g,g);
0162     return {p};
0163   }
0164 
0165   void erase(handle_type)
0166   {
0167     /* unused entries taken care of by garbage collector */
0168   }
0169 
0170   static const Entry& entry(handle_type h){return h.get();}
0171 
0172 private:  
0173   container_type          cont;
0174   std::mutex              m;
0175   std::condition_variable cv;
0176   bool                    stop=false;
0177   std::thread             gc;
0178 };
0179 
0180 struct concurrent_factory_class_empty_base:factory_marker{};
0181 
0182 template<
0183   typename Entry,typename Key,
0184   typename Hash,typename Pred,typename Allocator
0185 >
0186 struct check_concurrent_factory_class_params{};
0187 
0188 } /* namespace concurrent_factory_detail */
0189 
0190 template<
0191   typename Entry,typename Key,
0192   typename Hash,typename Pred,typename Allocator
0193 >
0194 class concurrent_factory_class:
0195   /* boost::mpl::apply may instantiate concurrent_factory_class<...> even when
0196    * the type is a lambda expression, so we need to guard against the
0197    * implementation defining nonsensical typedefs based on placeholder params.
0198    */
0199 
0200   public std::conditional<
0201     boost::flyweights::detail::is_placeholder_expression<
0202       concurrent_factory_detail::check_concurrent_factory_class_params<
0203         Entry,Key,Hash,Pred,Allocator
0204       >
0205     >::value,
0206     concurrent_factory_detail::concurrent_factory_class_empty_base,
0207     concurrent_factory_detail::concurrent_factory_class_impl<
0208       Entry,Key,Hash,Pred,Allocator
0209     >
0210   >::type
0211 {};
0212 
0213 template<
0214   typename Entry,typename Key,
0215   typename Hash,typename Pred,typename Allocator,
0216   typename F
0217 >
0218 typename concurrent_factory_class<Entry,Key,Hash,Pred,Allocator>::handle_type
0219 insert_and_visit(
0220   concurrent_factory_class<Entry,Key,Hash,Pred,Allocator>& fac,Entry&& x,F f)
0221 {
0222   return fac.insert_and_visit(std::move(x),f);
0223 }
0224 
0225 /* concurrent_factory_class specifier */
0226 
0227 template<
0228   typename Hash,typename Pred,typename Allocator
0229   BOOST_FLYWEIGHT_NOT_A_PLACEHOLDER_EXPRESSION_DEF
0230 >
0231 struct concurrent_factory:factory_marker
0232 {
0233   template<typename Entry,typename Key>
0234   struct apply:
0235     mpl::apply2<
0236       concurrent_factory_class<
0237         boost::mpl::_1,boost::mpl::_2,Hash,Pred,Allocator
0238       >,
0239       Entry,Key
0240     >
0241   {};
0242 };
0243 
0244 } /* namespace flyweights */
0245 
0246 } /* namespace boost */
0247 
0248 #endif