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/null_output_iterator.hpp>
22 #include <boost/signals2/detail/unique_lock.hpp>
23 #include <boost/signals2/slot.hpp>
24 #include <boost/weak_ptr.hpp>
25 
26 namespace boost
27 {
28   namespace signals2
29   {
null_deleter(const void *)30     extern inline void null_deleter(const void*) {}
31     namespace detail
32     {
33       class connection_body_base
34       {
35       public:
connection_body_base()36         connection_body_base():
37           _connected(true)
38         {
39         }
~connection_body_base()40         virtual ~connection_body_base() {}
disconnect()41         void disconnect()
42         {
43           unique_lock<connection_body_base> local_lock(*this);
44           nolock_disconnect();
45         }
nolock_disconnect()46         void nolock_disconnect()
47         {
48           _connected = false;
49         }
50         virtual bool connected() const = 0;
get_blocker()51         shared_ptr<void> get_blocker()
52         {
53           unique_lock<connection_body_base> local_lock(*this);
54           shared_ptr<void> blocker = _weak_blocker.lock();
55           if(blocker == shared_ptr<void>())
56           {
57             blocker.reset(this, &null_deleter);
58             _weak_blocker = blocker;
59           }
60           return blocker;
61         }
blocked() const62         bool blocked() const
63         {
64           return !_weak_blocker.expired();
65         }
nolock_nograb_blocked() const66         bool nolock_nograb_blocked() const
67         {
68           return nolock_nograb_connected() == false || blocked();
69         }
nolock_nograb_connected() const70         bool nolock_nograb_connected() const {return _connected;}
71         // expose part of Lockable concept of mutex
72         virtual void lock() = 0;
73         virtual void unlock() = 0;
74 
75       protected:
76 
77         mutable bool _connected;
78         weak_ptr<void> _weak_blocker;
79       };
80 
81       template<typename GroupKey, typename SlotType, typename Mutex>
82       class connection_body: public connection_body_base
83       {
84       public:
85         typedef Mutex mutex_type;
connection_body(const SlotType & slot_in)86         connection_body(const SlotType &slot_in):
87           slot(slot_in)
88         {
89         }
~connection_body()90         virtual ~connection_body() {}
connected() const91         virtual bool connected() const
92         {
93           unique_lock<mutex_type> local_lock(_mutex);
94           nolock_grab_tracked_objects(detail::null_output_iterator());
95           return nolock_nograb_connected();
96         }
group_key() const97         const GroupKey& group_key() const {return _group_key;}
set_group_key(const GroupKey & key)98         void set_group_key(const GroupKey &key) {_group_key = key;}
nolock_slot_expired() const99         bool nolock_slot_expired() const
100         {
101           bool expired = slot.expired();
102           if(expired == true)
103           {
104             _connected = false;
105           }
106           return expired;
107         }
108         template<typename OutputIterator>
nolock_grab_tracked_objects(OutputIterator inserter) const109           void nolock_grab_tracked_objects(OutputIterator inserter) const
110         {
111           slot_base::tracked_container_type::const_iterator it;
112           for(it = slot.tracked_objects().begin();
113             it != slot.tracked_objects().end();
114             ++it)
115           {
116             void_shared_ptr_variant locked_object
117             (
118               apply_visitor
119               (
120                 detail::lock_weak_ptr_visitor(),
121                 *it
122               )
123             );
124             if(apply_visitor(detail::expired_weak_ptr_visitor(), *it))
125             {
126               _connected = false;
127               return;
128             }
129             *inserter++ = locked_object;
130           }
131         }
132         // expose Lockable concept of mutex
lock()133         virtual void lock()
134         {
135           _mutex.lock();
136         }
unlock()137         virtual void unlock()
138         {
139           _mutex.unlock();
140         }
141         SlotType slot;
142       private:
143         mutable mutex_type _mutex;
144         GroupKey _group_key;
145       };
146     }
147 
148     class shared_connection_block;
149 
150     class connection
151     {
152     public:
153       friend class shared_connection_block;
154 
connection()155       connection() {}
connection(const connection & other)156       connection(const connection &other): _weak_connection_body(other._weak_connection_body)
157       {}
connection(const boost::weak_ptr<detail::connection_body_base> & connectionBody)158       connection(const boost::weak_ptr<detail::connection_body_base> &connectionBody):
159         _weak_connection_body(connectionBody)
160       {}
~connection()161       ~connection() {}
disconnect() const162       void disconnect() const
163       {
164         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
165         if(connectionBody == 0) return;
166         connectionBody->disconnect();
167       }
connected() const168       bool connected() const
169       {
170         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
171         if(connectionBody == 0) return false;
172         return connectionBody->connected();
173       }
blocked() const174       bool blocked() const
175       {
176         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
177         if(connectionBody == 0) return true;
178         return connectionBody->blocked();
179       }
operator ==(const connection & other) const180       bool operator==(const connection& other) const
181       {
182         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
183         boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock());
184         return connectionBody == otherConnectionBody;
185       }
operator !=(const connection & other) const186       bool operator!=(const connection& other) const
187       {
188         return !(*this == other);
189       }
operator <(const connection & other) const190       bool operator<(const connection& other) const
191       {
192         boost::shared_ptr<detail::connection_body_base> connectionBody(_weak_connection_body.lock());
193         boost::shared_ptr<detail::connection_body_base> otherConnectionBody(other._weak_connection_body.lock());
194         return connectionBody < otherConnectionBody;
195       }
swap(connection & other)196       void swap(connection &other)
197       {
198         using std::swap;
199         swap(_weak_connection_body, other._weak_connection_body);
200       }
201     protected:
202 
203       boost::weak_ptr<detail::connection_body_base> _weak_connection_body;
204     };
swap(connection & conn1,connection & conn2)205     inline void swap(connection &conn1, connection &conn2)
206     {
207       conn1.swap(conn2);
208     }
209 
210     class scoped_connection: public connection
211     {
212     public:
scoped_connection()213       scoped_connection() {}
scoped_connection(const connection & other)214       scoped_connection(const connection &other):
215         connection(other)
216       {}
~scoped_connection()217       ~scoped_connection()
218       {
219         disconnect();
220       }
operator =(const connection & rhs)221       scoped_connection& operator=(const connection &rhs)
222       {
223         disconnect();
224         connection::operator=(rhs);
225         return *this;
226       }
release()227       connection release()
228       {
229         connection conn(_weak_connection_body);
230         _weak_connection_body.reset();
231         return conn;
232       }
233     private:
234       scoped_connection(const scoped_connection &other);
235       scoped_connection& operator=(const scoped_connection &rhs);
236     };
237     // Sun 5.9 compiler doesn't find the swap for base connection class when
238     // arguments are scoped_connection, so we provide this explicitly.
swap(scoped_connection & conn1,scoped_connection & conn2)239     inline void swap(scoped_connection &conn1, scoped_connection &conn2)
240     {
241       conn1.swap(conn2);
242     }
243   }
244 }
245 
246 #endif  // BOOST_SIGNALS2_CONNECTION_HPP
247