2 * Copyright 2003, The libsigc++ Development Team
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.
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.
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
18 #include <sigc++/signal_base.h>
19 #include <memory> // std::unique_ptr
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.
30 signal_impl::iterator_type iter_;
32 self_and_iter(signal_impl* self, signal_impl::iterator_type iter)
33 : self_(self), iter_(iter) {}
36 signal_impl::signal_impl()
37 : ref_count_(0), exec_count_(0), deferred_(false)
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_)
47 void signal_impl::operator delete(void* p)
53 void signal_impl::clear()
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);
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_)
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)
72 deferred_ = saved_deferred;
77 signal_impl::size_type signal_impl::size() const noexcept
82 bool signal_impl::blocked() const noexcept
84 for (const auto& slot : const_cast<const std::list<slot_base>&>(slots_))
92 void signal_impl::block(bool should_block) noexcept
94 for (auto& slot : slots_)
96 slot.block(should_block);
100 signal_impl::iterator_type signal_impl::connect(const slot_base& slot_)
102 return insert(slots_.end(), slot_);
105 signal_impl::iterator_type signal_impl::connect(slot_base&& slot_)
107 return insert(slots_.end(), std::move(slot_));
110 signal_impl::iterator_type signal_impl::erase(iterator_type i)
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);
117 // Disconnect the slot before it is deleted.
118 // signal_impl::notify() will be called and delete the self_and_iter struct.
121 deferred_ = saved_deferred;
123 return slots_.erase(i);
126 signal_impl::iterator_type signal_impl::insert(signal_impl::iterator_type i, const slot_base& slot_)
128 auto temp = slots_.insert(i, slot_);
129 auto si = new self_and_iter(this, temp);
130 temp->set_parent(si, ¬ify);
134 signal_impl::iterator_type signal_impl::insert(signal_impl::iterator_type i, slot_base&& slot_)
136 auto temp = slots_.insert(i, std::move(slot_));
137 auto si = new self_and_iter(this, temp);
138 temp->set_parent(si, ¬ify);
142 void signal_impl::sweep()
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);
150 auto i = slots_.begin();
151 while (i != slots_.end())
159 void* signal_impl::notify(void* d)
161 std::unique_ptr<self_and_iter> si(static_cast<self_and_iter*>(d));
163 if (si->self_->exec_count_ == 0)
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_);
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().
177 } /* namespace internal */
179 signal_base::signal_base() noexcept
183 signal_base::signal_base(const signal_base& src) noexcept
190 signal_base::signal_base(signal_base&& src)
191 : trackable(std::move(src)),
192 impl_(std::move(src.impl_))
197 signal_base::~signal_base()
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)
206 impl_->unreference();
210 void signal_base::clear()
216 signal_base::size_type signal_base::size() const noexcept
218 return (impl_ ? impl_->size() : 0);
221 bool signal_base::blocked() const noexcept
223 return (impl_ ? impl_->blocked() : true);
226 void signal_base::block(bool should_block) noexcept
229 impl_->block(should_block);
232 void signal_base::unblock() noexcept
238 signal_base::iterator_type signal_base::connect(const slot_base& slot_)
240 return impl()->connect(slot_);
243 signal_base::iterator_type signal_base::connect(slot_base&& slot_)
245 return impl()->connect(std::move(slot_));
248 signal_base::iterator_type signal_base::insert(iterator_type i, const slot_base& slot_)
250 return impl()->insert(i, slot_);
253 signal_base::iterator_type signal_base::insert(iterator_type i, slot_base&& slot_)
255 return impl()->insert(i, std::move(slot_));
258 signal_base::iterator_type signal_base::erase(iterator_type i)
260 return impl()->erase(i);
263 signal_base& signal_base::operator=(const signal_base& src)
265 if (src.impl_ == impl_) return *this;
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)
274 impl_->unreference();
281 signal_base& signal_base::operator=(signal_base&& src)
283 if (src.impl_ == impl_) return *this;
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)
292 impl_->unreference();
295 src.notify_callbacks();
302 internal::signal_impl* signal_base::impl() const
305 impl_ = new internal::signal_impl;
306 impl_->reference(); // start with a reference count of 1