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 3553662 : garbage_collecting_lock(Mutex &m): 44 1776831 : lock(m) 45 3553662 : {} 46 4250 : void add_trash(const shared_ptr<void> &piece_of_trash) 47 : { 48 4250 : garbage.push_back(piece_of_trash); 49 4250 : } 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 59554 : connection_body_base(): 62 29777 : _connected(true), m_slot_refcount(1) 63 29777 : { 64 29777 : } 65 7694 : virtual ~connection_body_base() {} 66 4250 : void disconnect() 67 : { 68 4250 : garbage_collecting_lock<connection_body_base> local_lock(*this); 69 4250 : nolock_disconnect(local_lock); 70 4250 : } 71 : template<typename Mutex> 72 4250 : void nolock_disconnect(garbage_collecting_lock<Mutex> &lock_arg) const 73 : { 74 4250 : if(_connected) 75 : { 76 4250 : _connected = false; 77 4250 : dec_slot_refcount(lock_arg); 78 4250 : } 79 4250 : } 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 375533 : bool blocked() const 93 : { 94 375533 : return !_weak_blocker.expired(); 95 : } 96 375533 : bool nolock_nograb_blocked() const 97 : { 98 375533 : return nolock_nograb_connected() == false || blocked(); 99 : } 100 1078778 : 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 375533 : void inc_slot_refcount(const garbage_collecting_lock<Mutex> &) 113 : { 114 375533 : BOOST_ASSERT(m_slot_refcount != 0); 115 375533 : ++m_slot_refcount; 116 375533 : } 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 379783 : void dec_slot_refcount(garbage_collecting_lock<Mutex> &lock_arg) const 123 : { 124 379783 : BOOST_ASSERT(m_slot_refcount != 0); 125 379783 : if(--m_slot_refcount == 0) 126 : { 127 4250 : lock_arg.add_trash(release_slot()); 128 4250 : } 129 379783 : } 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 89331 : connection_body(const SlotType &slot_in, const boost::shared_ptr<mutex_type> &signal_mutex): 146 29777 : m_slot(new SlotType(slot_in)), _mutex(signal_mutex) 147 59554 : { 148 59554 : } 149 15388 : 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 1066 : const GroupKey& group_key() const {return _group_key;} 157 29777 : void set_group_key(const GroupKey &key) {_group_key = key;} 158 : template<typename M> 159 5415 : void disconnect_expired_slot(garbage_collecting_lock<M> &lock_arg) 160 : { 161 5415 : if(!m_slot) return; 162 5079 : bool expired = slot().expired(); 163 5079 : if(expired == true) 164 : { 165 0 : nolock_disconnect(lock_arg); 166 0 : } 167 5415 : } 168 : template<typename M, typename OutputIterator> 169 375533 : void nolock_grab_tracked_objects(garbage_collecting_lock<M> &lock_arg, 170 : OutputIterator inserter) const 171 : { 172 375533 : if(!m_slot) return; 173 375533 : slot_base::tracked_container_type::const_iterator it; 174 480135 : for(it = slot().tracked_objects().begin(); 175 480135 : it != slot().tracked_objects().end(); 176 104602 : ++it) 177 : { 178 : void_shared_ptr_variant locked_object 179 : ( 180 104603 : apply_visitor 181 : ( 182 : detail::lock_weak_ptr_visitor(), 183 104603 : *it 184 : ) 185 : ); 186 104603 : if(apply_visitor(detail::expired_weak_ptr_visitor(), *it)) 187 : { 188 0 : nolock_disconnect(lock_arg); 189 0 : return; 190 : } 191 104603 : *inserter++ = locked_object; 192 104603 : } 193 375533 : } 194 : // expose Lockable concept of mutex 195 702074 : virtual void lock() 196 : { 197 702074 : _mutex->lock(); 198 702074 : } 199 702074 : virtual void unlock() 200 : { 201 702074 : _mutex->unlock(); 202 702074 : } 203 380612 : SlotType &slot() 204 : { 205 380612 : return *m_slot; 206 : } 207 855667 : const SlotType &slot() const 208 : { 209 855667 : return *m_slot; 210 : } 211 : protected: 212 4250 : virtual shared_ptr<void> release_slot() const 213 : { 214 : 215 4250 : shared_ptr<void> released_slot = m_slot; 216 4250 : m_slot.reset(); 217 4250 : return released_slot; 218 4250 : } 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 32126 : connection() BOOST_NOEXCEPT {} 234 : connection(const connection &other) BOOST_NOEXCEPT: _weak_connection_body(other._weak_connection_body) 235 : {} 236 59554 : connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody) BOOST_NOEXCEPT: 237 29777 : _weak_connection_body(connectionBody) 238 59554 : {} 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 14084 : connection & operator=(connection && other) BOOST_NOEXCEPT 249 : { 250 14084 : if(&other == this) return *this; 251 14084 : _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 14084 : other._weak_connection_body.reset(); 255 14084 : return *this; 256 14084 : } 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 65216 : ~connection() {} 266 6186 : void disconnect() const 267 : { 268 6186 : boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock()); 269 6186 : if(connectionBody == 0) return; 270 4250 : connectionBody->disconnect(); 271 6186 : } 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