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