]> git.tdb.fi Git - ext/sigc++-2.0.git/blob - sigc++/signal_base.cc
Adjust the name of the library to match upstream
[ext/sigc++-2.0.git] / sigc++ / signal_base.cc
1 /*
2  * Copyright 2003, The libsigc++ Development Team
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 #include <sigc++/signal_base.h>
19 #include <memory> // std::unique_ptr
20
21 namespace sigc {
22 namespace internal {
23
24 // Data sent from signal_impl::insert() to slot_rep::set_parent() when a slot is
25 // connected, and then sent from slot_rep::disconnect() to signal_impl::notify()
26 // when the slot is disconnected. Bug 167714.
27 struct self_and_iter
28 {
29   signal_impl* self_;
30   signal_impl::iterator_type iter_;
31
32   self_and_iter(signal_impl* self, signal_impl::iterator_type iter)
33     : self_(self), iter_(iter) {}
34 };
35
36 signal_impl::signal_impl()
37 : ref_count_(0), exec_count_(0), deferred_(false)
38 {}
39
40 // only MSVC needs this to guarantee that all new/delete are executed from the DLL module
41 #ifdef SIGC_NEW_DELETE_IN_LIBRARY_ONLY
42 void* signal_impl::operator new(size_t size_)
43 {
44   return malloc(size_);
45 }
46
47 void signal_impl::operator delete(void* p)
48 {
49   free(p);
50 }
51 #endif
52
53 void signal_impl::clear()
54 {
55   // Don't let signal_impl::notify() erase the slots. It would invalidate the
56   // iterator in the following loop.
57   const bool during_signal_emission = exec_count_ > 0;
58   const bool saved_deferred = deferred_;
59   signal_exec exec(this);
60
61   // Disconnect all connected slots before they are deleted.
62   // signal_impl::notify() will be called and delete the self_and_iter structs.
63   for (auto& slot : slots_)
64     slot.disconnect();
65
66   // Don't clear slots_ during signal emission. Provided deferred_ is true,
67   // sweep() will be called from ~signal_exec() after signal emission,
68   // and it will erase all disconnected slots.
69   // https://bugzilla.gnome.org/show_bug.cgi?id=784550
70   if (!during_signal_emission)
71   {
72     deferred_ = saved_deferred;
73     slots_.clear();
74   }
75 }
76
77 signal_impl::size_type signal_impl::size() const noexcept
78 {
79   return slots_.size();
80 }
81
82 bool signal_impl::blocked() const noexcept
83 {
84   for (const auto& slot : const_cast<const std::list<slot_base>&>(slots_))
85   {
86     if (!slot.blocked())
87       return false;
88   }
89   return true;
90 }
91
92 void signal_impl::block(bool should_block) noexcept
93 {
94   for (auto& slot : slots_)
95   {
96     slot.block(should_block);
97   }
98 }
99
100 signal_impl::iterator_type signal_impl::connect(const slot_base& slot_)
101 {
102   return insert(slots_.end(), slot_);
103 }
104
105 signal_impl::iterator_type signal_impl::connect(slot_base&& slot_)
106 {
107   return insert(slots_.end(), std::move(slot_));
108 }
109
110 signal_impl::iterator_type signal_impl::erase(iterator_type i)
111 {
112   // Don't let signal_impl::notify() erase the slot. It would be more
113   // difficult to get the correct return value from signal_impl::erase().
114   const bool saved_deferred = deferred_;
115   signal_exec exec(this);
116
117   // Disconnect the slot before it is deleted.
118   // signal_impl::notify() will be called and delete the self_and_iter struct.
119   i->disconnect();
120
121   deferred_ = saved_deferred;
122
123   return slots_.erase(i);
124 }
125     
126 signal_impl::iterator_type signal_impl::insert(signal_impl::iterator_type i, const slot_base& slot_)
127 {
128   auto temp = slots_.insert(i, slot_);
129   auto si = new self_and_iter(this, temp);
130   temp->set_parent(si, &notify);
131   return temp;
132 }
133
134 signal_impl::iterator_type signal_impl::insert(signal_impl::iterator_type i, slot_base&& slot_)
135 {
136   auto temp = slots_.insert(i, std::move(slot_));
137   auto si = new self_and_iter(this, temp);
138   temp->set_parent(si, &notify);
139   return temp;
140 }
141
142 void signal_impl::sweep()
143 {
144   // The deletion of a slot may cause the deletion of a signal_base,
145   // a decrementation of ref_count_, and the deletion of this.
146   // In that case, the deletion of this is deferred to ~signal_exec().
147   signal_exec exec(this);
148
149   deferred_ = false;
150   auto i = slots_.begin();
151   while (i != slots_.end())
152     if ((*i).empty())
153       i = slots_.erase(i);
154     else
155       ++i;
156 }
157
158 //static
159 void* signal_impl::notify(void* d)
160 {
161   std::unique_ptr<self_and_iter> si(static_cast<self_and_iter*>(d));
162
163   if (si->self_->exec_count_ == 0)
164   {
165     // The deletion of a slot may cause the deletion of a signal_base,
166     // a decrementation of si->self_->ref_count_, and the deletion of si->self_.
167     // In that case, the deletion of si->self_ is deferred to ~signal_exec().
168     signal_exec exec(si->self_);
169     si->self_->slots_.erase(si->iter_);
170   }
171   else                           // This is occuring during signal emission or slot erasure.
172     si->self_->deferred_ = true; // => sweep() will be called from ~signal_exec() after signal emission.
173   return nullptr;                // This is safer because we don't have to care about our
174                                  // iterators in emit(), clear(), and erase().
175 }
176
177 } /* namespace internal */
178
179 signal_base::signal_base() noexcept
180 : impl_(nullptr)
181 {}
182
183 signal_base::signal_base(const signal_base& src) noexcept
184 : trackable(),
185   impl_(src.impl())
186 {
187   impl_->reference();
188 }
189
190 signal_base::signal_base(signal_base&& src)
191 : trackable(std::move(src)),
192   impl_(std::move(src.impl_))
193 {
194   src.impl_ = nullptr;
195 }
196
197 signal_base::~signal_base()
198 {
199   if (impl_)
200   {
201     // Disconnect all slots before impl_ is deleted.
202     // TODO: Move the signal_impl::clear() call to ~signal_impl() when ABI can be broken.
203     if (impl_->ref_count_ == 1)
204       impl_->clear();
205
206     impl_->unreference();
207   }
208 }
209
210 void signal_base::clear()
211 {
212   if (impl_)
213     impl_->clear();
214 }
215
216 signal_base::size_type signal_base::size() const noexcept
217 {
218   return (impl_ ? impl_->size() : 0);
219 }
220
221 bool signal_base::blocked() const noexcept
222 {
223   return (impl_ ? impl_->blocked() : true);
224 }
225
226 void signal_base::block(bool should_block) noexcept
227 {
228   if (impl_)
229     impl_->block(should_block);
230 }
231
232 void signal_base::unblock() noexcept
233 {
234   if (impl_)
235     impl_->block(false);
236 }
237
238 signal_base::iterator_type signal_base::connect(const slot_base& slot_)
239 {
240   return impl()->connect(slot_);
241 }
242
243 signal_base::iterator_type signal_base::connect(slot_base&& slot_)
244 {
245   return impl()->connect(std::move(slot_));
246 }
247
248 signal_base::iterator_type signal_base::insert(iterator_type i, const slot_base& slot_)
249 {
250   return impl()->insert(i, slot_);
251 }
252
253 signal_base::iterator_type signal_base::insert(iterator_type i, slot_base&& slot_)
254 {
255   return impl()->insert(i, std::move(slot_));
256 }
257
258 signal_base::iterator_type signal_base::erase(iterator_type i)
259 {
260   return impl()->erase(i);
261 }
262
263 signal_base& signal_base::operator=(const signal_base& src)
264 {
265   if (src.impl_ == impl_) return *this;
266
267   if (impl_)
268   {
269     // Disconnect all slots before impl_ is deleted.
270     // TODO: Move the signal_impl::clear() call to ~signal_impl() when ABI can be broken.
271     if (impl_->ref_count_ == 1)
272       impl_->clear();
273
274     impl_->unreference();
275   }
276   impl_ = src.impl();
277   impl_->reference();
278   return *this;
279 }
280
281 signal_base& signal_base::operator=(signal_base&& src)
282 {
283   if (src.impl_ == impl_) return *this;
284
285   if (impl_)
286   {
287     // Disconnect all slots before impl_ is deleted.
288     // TODO: Move the signal_impl::clear() call to ~signal_impl() when ABI can be broken.
289     if (impl_->ref_count_ == 1)
290       impl_->clear();
291
292     impl_->unreference();
293   }
294
295   src.notify_callbacks();
296   impl_ = src.impl_;
297   src.impl_ = nullptr;
298
299   return *this;
300 }
301
302 internal::signal_impl* signal_base::impl() const
303 {
304   if (!impl_) {
305     impl_ = new internal::signal_impl;
306     impl_->reference();  // start with a reference count of 1
307   }
308   return impl_;
309 }
310
311 } /* sigc */