Line data Source code
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/core/noncopyable.hpp> 19 : #include <boost/function.hpp> 20 : #include <boost/mpl/bool.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 : { 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: 43 167700 : garbage_collecting_lock(Mutex &m): 44 83850 : lock(m) 45 167700 : {} 46 336 : void add_trash(const shared_ptr<void> &piece_of_trash) 47 : { 48 336 : garbage.push_back(piece_of_trash); 49 336 : } 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: 61 1424 : connection_body_base(): 62 712 : _connected(true), m_slot_refcount(1) 63 712 : { 64 712 : } 65 364 : virtual ~connection_body_base() {} 66 336 : void disconnect() 67 : { 68 336 : garbage_collecting_lock<connection_body_base> local_lock(*this); 69 336 : nolock_disconnect(local_lock); 70 336 : } 71 : template<typename Mutex> 72 336 : void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const 73 : { 74 336 : if(_connected) 75 : { 76 336 : _connected = false; 77 336 : dec_slot_refcount(lock_arg); 78 336 : } 79 336 : } 80 : virtual bool connected() const = 0; 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 : } 92 235 : bool blocked() const 93 : { 94 235 : return !_weak_blocker.expired(); 95 : } 96 235 : bool nolock_nograb_blocked() const 97 : { 98 235 : return nolock_nograb_connected() == false || blocked(); 99 : } 100 1041 : 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> 112 235 : void inc_slot_refcount(const garbage_collecting_lock<Mutex> &) 113 : { 114 235 : BOOST_ASSERT(m_slot_refcount != 0); 115 235 : ++m_slot_refcount; 116 235 : } 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> 122 571 : void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const 123 : { 124 571 : BOOST_ASSERT(m_slot_refcount != 0); 125 571 : if(--m_slot_refcount == 0) 126 : { 127 336 : lock_arg.add_trash(release_slot()); 128 336 : } 129 571 : } 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; 145 2136 : connection_body(const SlotType &slot_in, const boost::shared_ptr<mutex_type> &signal_mutex): 146 712 : m_slot(new SlotType(slot_in)), _mutex(signal_mutex) 147 1424 : { 148 1424 : } 149 728 : virtual ~connection_body() {} 150 0 : virtual bool connected() const 151 : { 152 0 : garbage_collecting_lock<mutex_type> local_lock(*_mutex); 153 0 : nolock_grab_tracked_objects(local_lock, detail::null_output_iterator()); 154 0 : return nolock_nograb_connected(); 155 0 : } 156 336 : const GroupKey& group_key() const {return _group_key;} 157 712 : void set_group_key(const GroupKey &key) {_group_key = key;} 158 : template<typename M> 159 336 : void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg) 160 : { 161 336 : if(!m_slot) return; 162 0 : bool expired = slot().expired(); 163 0 : if(expired == true) 164 : { 165 0 : nolock_disconnect(lock_arg); 166 0 : } 167 336 : } 168 : template<typename M, typename OutputIterator> 169 235 : void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg, 170 : OutputIterator inserter) const 171 : { 172 235 : if(!m_slot) return; 173 235 : slot_base::tracked_container_type::const_iterator it; 174 461 : for(it = slot().tracked_objects().begin(); 175 461 : it != slot().tracked_objects().end(); 176 226 : ++it) 177 : { 178 : void_shared_ptr_variant locked_object 179 : ( 180 226 : apply_visitor 181 : ( 182 : detail::lock_weak_ptr_visitor(), 183 226 : *it 184 : ) 185 : ); 186 226 : if(apply_visitor(detail::expired_weak_ptr_visitor(), *it)) 187 : { 188 0 : nolock_disconnect(lock_arg); 189 0 : return; 190 : } 191 226 : *inserter++ = locked_object; 192 226 : } 193 235 : } 194 : // expose Lockable concept of mutex 195 806 : virtual void lock() 196 : { 197 806 : _mutex->lock(); 198 806 : } 199 806 : virtual void unlock() 200 : { 201 806 : _mutex->unlock(); 202 806 : } 203 235 : SlotType &slot() 204 : { 205 235 : return *m_slot; 206 : } 207 696 : const SlotType &slot() const 208 : { 209 696 : return *m_slot; 210 : } 211 : protected: 212 336 : virtual shared_ptr<void> release_slot() const 213 : { 214 : 215 336 : shared_ptr<void> released_slot = m_slot; 216 336 : m_slot.reset(); 217 336 : return released_slot; 218 336 : } 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 : 233 1168 : connection() BOOST_NOEXCEPT {} 234 : connection(const connection &other) BOOST_NOEXCEPT: _weak_connection_body(other._weak_connection_body) 235 : {} 236 1424 : connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody) BOOST_NOEXCEPT: 237 712 : _weak_connection_body(connectionBody) 238 1424 : {} 239 : 240 : // move support 241 : #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 242 0 : connection(connection && other) BOOST_NOEXCEPT: _weak_connection_body(std::move(other._weak_connection_body)) 243 0 : { 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 0 : other._weak_connection_body.reset(); 247 0 : } 248 684 : connection & operator=(connection && other) BOOST_NOEXCEPT 249 : { 250 684 : if(&other == this) return *this; 251 684 : _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 684 : other._weak_connection_body.reset(); 255 684 : return *this; 256 684 : } 257 : #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 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 : 265 1424 : ~connection() {} 266 336 : void disconnect() const 267 : { 268 336 : boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 269 336 : if(connectionBody == 0) return; 270 336 : connectionBody->disconnect(); 271 336 : } 272 : 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 : } 278 : 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 : } 284 : 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 : } 290 : bool operator!=(const connection& other) const 291 : { 292 : return !(*this == other); 293 : } 294 : 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 : } 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 : }; 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: 317 : scoped_connection() BOOST_NOEXCEPT {} 318 : scoped_connection(const connection &other) BOOST_NOEXCEPT: 319 : connection(other) 320 : {} 321 0 : ~scoped_connection() 322 0 : { 323 0 : disconnect(); 324 0 : } 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) 334 : scoped_connection(scoped_connection && other) BOOST_NOEXCEPT: connection(std::move(other)) 335 : { 336 : } 337 0 : scoped_connection(connection && other) BOOST_NOEXCEPT: connection(std::move(other)) 338 0 : { 339 0 : } 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 : } 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 : 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. 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