connection.hpp 12 KB

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