File indexing completed on 2025-09-17 08:25:54
0001
0002
0003
0004
0005
0006
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
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
0077
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
0118
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
0136
0137 {
0138 std::unique_lock<std::mutex> lk{m};
0139 stop=true;
0140 }
0141 cv.notify_one();
0142 gc.join();
0143 }
0144
0145
0146
0147
0148
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
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 }
0189
0190 template<
0191 typename Entry,typename Key,
0192 typename Hash,typename Pred,typename Allocator
0193 >
0194 class concurrent_factory_class:
0195
0196
0197
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
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 }
0245
0246 }
0247
0248 #endif