1 /* 2 boost::signals2::connection provides a handle to a signal/slot connection. 3 4 Author: Frank Mori Hess <fmhess@users.sourceforge.net> 5 Begin: 2007-01-23 6 */ 7 // Copyright Frank Mori Hess 2007-2008. 8 // Distributed under the Boost Software License, Version 9 // 1.0. (See accompanying file LICENSE_1_0.txt or copy at 10 // http://www.boost.org/LICENSE_1_0.txt) 11 12 // See http://www.boost.org/libs/signals2 for library home page. 13 14 #ifndef BOOST_SIGNALS2_CONNECTION_HPP 15 #define BOOST_SIGNALS2_CONNECTION_HPP 16 17 #include <boost/config.hpp> 18 #include <boost/function.hpp> 19 #include <boost/mpl/bool.hpp> 20 #include <boost/noncopyable.hpp> 21 #include <boost/shared_ptr.hpp> 22 #include <boost/signals2/detail/auto_buffer.hpp> 23 #include <boost/signals2/detail/null_output_iterator.hpp> 24 #include <boost/signals2/detail/unique_lock.hpp> 25 #include <boost/signals2/slot.hpp> 26 #include <boost/weak_ptr.hpp> 27 28 namespace boost 29 { 30 namespace signals2 31 { null_deleter(const void *)32 inline void null_deleter(const void*) {} 33 namespace detail 34 { 35 // This lock maintains a list of shared_ptr<void> 36 // which will be destroyed only after the lock 37 // has released its mutex. Used to garbage 38 // collect disconnected slots 39 template<typename Mutex> 40 class garbage_collecting_lock: public noncopyable 41 { 42 public: garbage_collecting_lock(Mutex & m)43 garbage_collecting_lock(Mutex &m): 44 lock(m) 45 {} add_trash(const shared_ptr<void> & piece_of_trash)46 void add_trash(const shared_ptr<void> &piece_of_trash) 47 { 48 garbage.push_back(piece_of_trash); 49 } 50 private: 51 // garbage must be declared before lock 52 // to insure it is destroyed after lock is 53 // destroyed. 54 auto_buffer<shared_ptr<void>, store_n_objects<10> > garbage; 55 unique_lock<Mutex> lock; 56 }; 57 58 class connection_body_base 59 { 60 public: connection_body_base()61 connection_body_base(): 62 _connected(true), m_slot_refcount(1) 63 { 64 } ~connection_body_base()65 virtual ~connection_body_base() {} disconnect()66 void disconnect() 67 { 68 garbage_collecting_lock<connection_body_base> local_lock(*this); 69 nolock_disconnect(local_lock); 70 } 71 template<typename Mutex> nolock_disconnect(garbage_collecting_lock<Mutex> & lock_arg) const72 void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const 73 { 74 if(_connected) 75 { 76 _connected = false; 77 dec_slot_refcount(lock_arg); 78 } 79 } 80 virtual bool connected() const = 0; get_blocker()81 shared_ptr<void> get_blocker() 82 { 83 unique_lock<connection_body_base> local_lock(*this); 84 shared_ptr<void> blocker = _weak_blocker.lock(); 85 if(blocker == shared_ptr<void>()) 86 { 87 blocker.reset(this, &null_deleter); 88 _weak_blocker = blocker; 89 } 90 return blocker; 91 } blocked() const92 bool blocked() const 93 { 94 return !_weak_blocker.expired(); 95 } nolock_nograb_blocked() const96 bool nolock_nograb_blocked() const 97 { 98 return nolock_nograb_connected() == false || blocked(); 99 } nolock_nograb_connected() const100 bool nolock_nograb_connected() const {return _connected;} 101 // expose part of Lockable concept of mutex 102 virtual void lock() = 0; 103 virtual void unlock() = 0; 104 105 // Slot refcount should be incremented while 106 // a signal invocation is using the slot, in order 107 // to prevent slot from being destroyed mid-invocation. 108 // garbage_collecting_lock parameter enforces 109 // the existance of a lock before this 110 // method is called 111 template<typename Mutex> inc_slot_refcount(const garbage_collecting_lock<Mutex> &)112 void inc_slot_refcount(const garbage_collecting_lock<Mutex> &) 113 { 114 BOOST_ASSERT(m_slot_refcount != 0); 115 ++m_slot_refcount; 116 } 117 // if slot refcount decrements to zero due to this call, 118 // it puts a 119 // shared_ptr to the slot in the garbage collecting lock, 120 // which will destroy the slot only after it unlocks. 121 template<typename Mutex> dec_slot_refcount(garbage_collecting_lock<Mutex> & lock_arg) const122 void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const 123 { 124 BOOST_ASSERT(m_slot_refcount != 0); 125 if(--m_slot_refcount == 0) 126 { 127 lock_arg.add_trash(release_slot()); 128 } 129 } 130 131 protected: 132 virtual shared_ptr<void> release_slot() const = 0; 133 134 weak_ptr<void> _weak_blocker; 135 private: 136 mutable bool _connected; 137 mutable unsigned m_slot_refcount; 138 }; 139 140 template<typename GroupKey, typename SlotType, typename Mutex> 141 class connection_body: public connection_body_base 142 { 143 public: 144 typedef Mutex mutex_type; connection_body(const SlotType & slot_in,const boost::shared_ptr<mutex_type> & signal_mutex)145 connection_body(const SlotType &slot_in, const boost::shared_ptr<mutex_type> &signal_mutex): 146 m_slot(new SlotType(slot_in)), _mutex(signal_mutex) 147 { 148 } ~connection_body()149 virtual ~connection_body() {} connected() const150 virtual bool connected() const 151 { 152 garbage_collecting_lock<mutex_type> local_lock(*_mutex); 153 nolock_grab_tracked_objects(local_lock, detail::null_output_iterator()); 154 return nolock_nograb_connected(); 155 } group_key() const156 const GroupKey& group_key() const {return _group_key;} set_group_key(const GroupKey & key)157 void set_group_key(const GroupKey &key) {_group_key = key;} 158 template<typename M> disconnect_expired_slot(garbage_collecting_lock<M> & lock_arg)159 void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg) 160 { 161 if(!m_slot) return; 162 bool expired = slot().expired(); 163 if(expired == true) 164 { 165 nolock_disconnect(lock_arg); 166 } 167 } 168 template<typename M, typename OutputIterator> nolock_grab_tracked_objects(garbage_collecting_lock<M> & lock_arg,OutputIterator inserter) const169 void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg, 170 OutputIterator inserter) const 171 { 172 if(!m_slot) return; 173 slot_base::tracked_container_type::const_iterator it; 174 for(it = slot().tracked_objects().begin(); 175 it != slot().tracked_objects().end(); 176 ++it) 177 { 178 void_shared_ptr_variant locked_object 179 ( 180 apply_visitor 181 ( 182 detail::lock_weak_ptr_visitor(), 183 *it 184 ) 185 ); 186 if(apply_visitor(detail::expired_weak_ptr_visitor(), *it)) 187 { 188 nolock_disconnect(lock_arg); 189 return; 190 } 191 *inserter++ = locked_object; 192 } 193 } 194 // expose Lockable concept of mutex lock()195 virtual void lock() 196 { 197 _mutex->lock(); 198 } unlock()199 virtual void unlock() 200 { 201 _mutex->unlock(); 202 } slot()203 SlotType &slot() 204 { 205 return *m_slot; 206 } slot() const207 const SlotType &slot() const 208 { 209 return *m_slot; 210 } 211 protected: release_slot() const212 virtual shared_ptr<void> release_slot() const 213 { 214 215 shared_ptr<void> released_slot = m_slot; 216 m_slot.reset(); 217 return released_slot; 218 } 219 private: 220 mutable boost::shared_ptr<SlotType> m_slot; 221 const boost::shared_ptr<mutex_type> _mutex; 222 GroupKey _group_key; 223 }; 224 } 225 226 class shared_connection_block; 227 228 class connection 229 { 230 public: 231 friend class shared_connection_block; 232 connection()233 connection() BOOST_NOEXCEPT {} connection(const connection & other)234 connection(const connection &other) BOOST_NOEXCEPT: _weak_connection_body(other._weak_connection_body) 235 {} connection(const boost::weak_ptr<detail::connection_body_base> & connectionBody)236 connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody) BOOST_NOEXCEPT: 237 _weak_connection_body(connectionBody) 238 {} 239 240 // move support 241 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) connection(connection && other)242 connection(connection && other) BOOST_NOEXCEPT: _weak_connection_body(std::move(other._weak_connection_body)) 243 { 244 // make sure other is reset, in case it is a scoped_connection (so it 245 // won't disconnect on destruction after being moved away from). 246 other._weak_connection_body.reset(); 247 } operator =(connection && other)248 connection & operator=(connection && other) BOOST_NOEXCEPT 249 { 250 if(&other == this) return *this; 251 _weak_connection_body = std::move(other._weak_connection_body); 252 // make sure other is reset, in case it is a scoped_connection (so it 253 // won't disconnect on destruction after being moved away from). 254 other._weak_connection_body.reset(); 255 return *this; 256 } 257 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) operator =(const connection & other)258 connection & operator=(const connection & other) BOOST_NOEXCEPT 259 { 260 if(&other == this) return *this; 261 _weak_connection_body = other._weak_connection_body; 262 return *this; 263 } 264 ~connection()265 ~connection() {} disconnect() const266 void disconnect() const 267 { 268 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 269 if(connectionBody == 0) return; 270 connectionBody->disconnect(); 271 } connected() const272 bool connected() const 273 { 274 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 275 if(connectionBody == 0) return false; 276 return connectionBody->connected(); 277 } blocked() const278 bool blocked() const 279 { 280 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 281 if(connectionBody == 0) return true; 282 return connectionBody->blocked(); 283 } operator ==(const connection & other) const284 bool operator==(const connection& other) const 285 { 286 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 287 boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock()); 288 return connectionBody == otherConnectionBody; 289 } operator !=(const connection & other) const290 bool operator!=(const connection& other) const 291 { 292 return !(*this == other); 293 } operator <(const connection & other) const294 bool operator<(const connection& other) const 295 { 296 boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 297 boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock()); 298 return connectionBody < otherConnectionBody; 299 } swap(connection & other)300 void swap(connection &other) BOOST_NOEXCEPT 301 { 302 using std::swap; 303 swap(_weak_connection_body, other._weak_connection_body); 304 } 305 protected: 306 307 boost::weak_ptr<detail::connection_body_base> _weak_connection_body; 308 }; swap(connection & conn1,connection & conn2)309 inline void swap(connection &conn1, connection &conn2) BOOST_NOEXCEPT 310 { 311 conn1.swap(conn2); 312 } 313 314 class scoped_connection: public connection 315 { 316 public: scoped_connection()317 scoped_connection() BOOST_NOEXCEPT {} scoped_connection(const connection & other)318 scoped_connection(const connection &other) BOOST_NOEXCEPT: 319 connection(other) 320 {} ~scoped_connection()321 ~scoped_connection() 322 { 323 disconnect(); 324 } operator =(const connection & rhs)325 scoped_connection& operator=(const connection &rhs) BOOST_NOEXCEPT 326 { 327 disconnect(); 328 connection::operator=(rhs); 329 return *this; 330 } 331 332 // move support 333 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) scoped_connection(scoped_connection && other)334 scoped_connection(scoped_connection && other) BOOST_NOEXCEPT: connection(std::move(other)) 335 { 336 } scoped_connection(connection && other)337 scoped_connection(connection && other) BOOST_NOEXCEPT: connection(std::move(other)) 338 { 339 } operator =(scoped_connection && other)340 scoped_connection & operator=(scoped_connection && other) BOOST_NOEXCEPT 341 { 342 if(&other == this) return *this; 343 disconnect(); 344 connection::operator=(std::move(other)); 345 return *this; 346 } operator =(connection && other)347 scoped_connection & operator=(connection && other) BOOST_NOEXCEPT 348 { 349 if(&other == this) return *this; 350 disconnect(); 351 connection::operator=(std::move(other)); 352 return *this; 353 } 354 #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 355 release()356 connection release() 357 { 358 connection conn(_weak_connection_body); 359 _weak_connection_body.reset(); 360 return conn; 361 } 362 private: 363 scoped_connection(const scoped_connection &other); 364 scoped_connection& operator=(const scoped_connection &rhs); 365 }; 366 // Sun 5.9 compiler doesn't find the swap for base connection class when 367 // arguments are scoped_connection, so we provide this explicitly. swap(scoped_connection & conn1,scoped_connection & conn2)368 inline void swap(scoped_connection &conn1, scoped_connection &conn2) BOOST_NOEXCEPT 369 { 370 conn1.swap(conn2); 371 } 372 } 373 } 374 375 #endif // BOOST_SIGNALS2_CONNECTION_HPP 376