connection.hpp 12 KB

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