]> git.lyx.org Git - lyx.git/blob - boost/libs/signals/src/signal_base.cpp
scons-based build system, by Bo Peng (ben.bob@gmail.com)
[lyx.git] / boost / libs / signals / src / signal_base.cpp
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 {
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
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
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::
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
102       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
115       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
128       void signal_base_impl::disconnect(const stored_group& group)
129       { slots_.disconnect(group); }
130
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
152       void signal_base_impl::remove_disconnected_slots() const
153       { slots_.remove_disconnected_slots(); }
154
155       call_notification::
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
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
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
182     signal_base::~signal_base()
183     {
184     }
185
186     } // namespace detail
187   } // namespace BOOST_SIGNALS_NAMESPACE
188 } // namespace boost
189