1 // Boost.Signals library
2 
3 // Copyright Douglas Gregor 2001-2004. Use, modification and
4 // distribution is subject to the Boost Software License, Version
5 // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
8 // For more information, see http://www.boost.org
9 
10 #define BOOST_SIGNALS_SOURCE
11 
12 #include <boost/signals/detail/signal_base.hpp>
13 #include <cassert>
14 
15 namespace boost {
16   namespace BOOST_SIGNALS_NAMESPACE {
17     namespace detail {
signal_base_impl(const compare_type & comp,const any & combiner)18       signal_base_impl::signal_base_impl(const compare_type& comp,
19                                          const any& combiner)
20         : call_depth(0),
21           slots_(comp),
22           combiner_(combiner)
23       {
24         flags.delayed_disconnect = false;
25         flags.clearing = false;
26       }
27 
~signal_base_impl()28       signal_base_impl::~signal_base_impl()
29       {
30         // Set the "clearing" flag to ignore extraneous disconnect requests,
31         // because all slots will be disconnected on destruction anyway.
32         flags.clearing = true;
33       }
34 
disconnect_all_slots()35       void signal_base_impl::disconnect_all_slots()
36       {
37         // Do nothing if we're already clearing the slot list
38         if (flags.clearing)
39           return;
40 
41         if (call_depth == 0) {
42           // Clearing the slot list will disconnect all slots automatically
43           temporarily_set_clearing set_clearing(this);
44           slots_.clear();
45         }
46         else {
47           // We can't actually remove elements from the slot list because there
48           // are still iterators into the slot list that must not be
49           // invalidated by this operation. So just disconnect each slot
50           // without removing it from the slot list. When the call depth does
51           // reach zero, the call list will be cleared.
52           flags.delayed_disconnect = true;
53           temporarily_set_clearing set_clearing(this);
54           for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
55             i->first.disconnect();
56           }
57         }
58       }
59 
60       connection
61       signal_base_impl::
connect_slot(const any & slot_,const stored_group & name,shared_ptr<slot_base::data_t> data,connect_position at)62         connect_slot(const any& slot_,
63                      const stored_group& name,
64                      shared_ptr<slot_base::data_t> data,
65                      connect_position at)
66       {
67         // Transfer the burden of ownership to a local, scoped
68         // connection.
69         data->watch_bound_objects.set_controlling(false);
70         scoped_connection safe_connection(data->watch_bound_objects);
71 
72         // Allocate storage for an iterator that will hold the point of
73         // insertion of the slot into the list. This is used to later remove
74         // the slot when it is disconnected.
75         std::auto_ptr<iterator> saved_iter(new iterator);
76 
77         // Add the slot to the list.
78         iterator pos =
79           slots_.insert(name, data->watch_bound_objects, slot_, at);
80 
81         // The assignment operation here absolutely must not throw, which
82         // intuitively makes sense (because any container's insert method
83         // becomes impossible to use in an exception-safe manner without this
84         // assumption), but doesn't appear to be mentioned in the standard.
85         *saved_iter = pos;
86 
87         // Fill out the connection object appropriately. None of these
88         // operations can throw
89         data->watch_bound_objects.get_connection()->signal = this;
90         data->watch_bound_objects.get_connection()->signal_data =
91           saved_iter.release();
92         data->watch_bound_objects.get_connection()->signal_disconnect =
93           &signal_base_impl::slot_disconnected;
94 
95         // Make the copy of the connection in the list disconnect when it is
96         // destroyed. The local, scoped connection is then released
97         // because ownership has been transferred.
98         pos->first.set_controlling();
99         return safe_connection.release();
100       }
101 
empty() const102       bool signal_base_impl::empty() const
103       {
104         // Disconnected slots may still be in the list of slots if
105         //   a) this is called while slots are being invoked (call_depth > 0)
106         //   b) an exception was thrown in remove_disconnected_slots
107         for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
108           if (i->first.connected())
109             return false;
110         }
111 
112         return true;
113       }
114 
num_slots() const115       std::size_t signal_base_impl::num_slots() const
116       {
117         // Disconnected slots may still be in the list of slots if
118         //   a) this is called while slots are being invoked (call_depth > 0)
119         //   b) an exception was thrown in remove_disconnected_slots
120         std::size_t count = 0;
121         for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
122           if (i->first.connected())
123             ++count;
124         }
125         return count;
126       }
127 
disconnect(const stored_group & group)128       void signal_base_impl::disconnect(const stored_group& group)
129       { slots_.disconnect(group); }
130 
slot_disconnected(void * obj,void * data)131       void signal_base_impl::slot_disconnected(void* obj, void* data)
132       {
133         signal_base_impl* self = reinterpret_cast<signal_base_impl*>(obj);
134 
135         // We won't need the slot iterator after this
136         std::auto_ptr<iterator> slot(reinterpret_cast<iterator*>(data));
137 
138         // If we're flags.clearing, we don't bother updating the list of slots
139         if (!self->flags.clearing) {
140           // If we're in a call, note the fact that a slot has been deleted so
141           // we can come back later to remove the iterator
142           if (self->call_depth > 0) {
143             self->flags.delayed_disconnect = true;
144           }
145           else {
146             // Just remove the slot now, it's safe
147             self->slots_.erase(*slot);
148           }
149         }
150       }
151 
remove_disconnected_slots() const152       void signal_base_impl::remove_disconnected_slots() const
153       { slots_.remove_disconnected_slots(); }
154 
155       call_notification::
call_notification(const shared_ptr<signal_base_impl> & b)156         call_notification(const shared_ptr<signal_base_impl>& b) :
157           impl(b)
158       {
159         // A call will be made, so increment the call depth as a notification
160         impl->call_depth++;
161       }
162 
~call_notification()163       call_notification::~call_notification()
164       {
165         impl->call_depth--;
166 
167         // If the call depth is zero and we have some slots that have been
168         // disconnected during the calls, remove those slots from the list
169         if (impl->call_depth == 0 &&
170             impl->flags.delayed_disconnect) {
171           impl->remove_disconnected_slots();
172           impl->flags.delayed_disconnect = false;
173         }
174       }
175 
signal_base(const compare_type & comp,const any & combiner)176     signal_base::signal_base(const compare_type& comp, const any& combiner)
177       : impl()
178     {
179       impl.reset(new signal_base_impl(comp, combiner));
180     }
181 
~signal_base()182     signal_base::~signal_base()
183     {
184     }
185 
186     } // namespace detail
187   } // namespace BOOST_SIGNALS_NAMESPACE
188 } // namespace boost
189 
190