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
20 #include <sigc++/functors/slot_base.h>
24 // Used by slot_rep::notify() and slot_base::operator=(). They must be
25 // notified, if the slot_rep is deleted when they call disconnect().
26 struct destroy_notify_struct
28 destroy_notify_struct() noexcept : deleted_(false) { }
30 static void* notify(void* data) noexcept
32 auto self_ = reinterpret_cast<destroy_notify_struct*>(data);
33 self_->deleted_ = true;
40 // Used by slot_base::set_parent() when a slot_base without a rep_ is assigned a parent.
41 class dummy_slot_rep : public sigc::internal::slot_rep
44 dummy_slot_rep() : slot_rep(nullptr, nullptr, &clone) {}
45 static void* clone(void*) { return new dummy_slot_rep(); }
47 } // anonymous namespace
53 // only MSVC needs this to guarantee that all new/delete are executed from the DLL module
54 #ifdef SIGC_NEW_DELETE_IN_LIBRARY_ONLY
55 void* slot_rep::operator new(size_t size_)
60 void slot_rep::operator delete(void* p)
66 void slot_rep::disconnect()
68 // Invalidate the slot.
69 // _Must_ be done here because parent_ might defer the actual
70 // destruction of the slot_rep and try to invoke it before that point.
71 // Must be done also for a slot without a parent, according to
72 // https://bugzilla.gnome.org/show_bug.cgi?id=311057
73 // See also https://bugzilla.gnome.org/show_bug.cgi?id=738602
79 parent_ = nullptr; // Just a precaution.
80 (cleanup_)(data_); // Notify the parent (might lead to destruction of this!).
85 void* slot_rep::notify(void* data)
87 auto self_ = reinterpret_cast<slot_rep*>(data);
89 self_->call_ = nullptr; // Invalidate the slot.
91 // Make sure we are notified if disconnect() deletes self_, which is trackable.
92 destroy_notify_struct notifier;
93 self_->add_destroy_notify_callback(¬ifier, destroy_notify_struct::notify);
94 self_->disconnect(); // Disconnect the slot (might lead to deletion of self_!).
95 // If self_ has been deleted, the destructor has called destroy().
96 if (!notifier.deleted_)
98 self_->remove_destroy_notify_callback(¬ifier);
99 self_->destroy(); // Detach the stored functor from the other referred trackables and destroy it.
100 // destroy() might lead to deletion of self_. Bug #564005.
105 } // namespace internal
107 slot_base::slot_base() noexcept
112 slot_base::slot_base(rep_type* rep) noexcept
117 slot_base::slot_base(const slot_base& src)
119 blocked_(src.blocked_)
123 //Check call_ so we can ignore invalidated slots.
124 //Otherwise, destroyed bound reference parameters (whose destruction caused the slot's invalidation) may be used during dup().
125 //Note: I'd prefer to check somewhere during dup(). murrayc.
127 rep_ = src.rep_->dup();
130 *this = slot_base(); //Return the default invalid slot.
135 slot_base::slot_base(slot_base&& src)
137 blocked_(src.blocked_)
141 if (src.rep_->parent_)
143 // src is connected to a parent, e.g. a sigc::signal.
144 // Copy, don't move! See https://bugzilla.gnome.org/show_bug.cgi?id=756484
146 //Check call_ so we can ignore invalidated slots.
147 //Otherwise, destroyed bound reference parameters (whose destruction
148 //caused the slot's invalidation) may be used during dup().
150 rep_ = src.rep_->dup();
152 blocked_ = false; //Return the default invalid slot.
156 // src is not connected. Really move src.rep_.
157 src.rep_->notify_callbacks();
162 src.blocked_ = false;
167 slot_base::~slot_base()
173 slot_base::operator bool() const noexcept
175 return rep_ != nullptr;
178 void slot_base::delete_rep_with_check()
183 // Make sure we are notified if disconnect() deletes rep_, which is trackable.
184 // Compare slot_rep::notify().
185 destroy_notify_struct notifier;
186 rep_->add_destroy_notify_callback(¬ifier, destroy_notify_struct::notify);
187 rep_->disconnect(); // Disconnect the slot (might lead to deletion of rep_!).
189 // If rep_ has been deleted, don't try to delete it again.
190 // If it has been deleted, this slot_base has probably also been deleted, so
191 // don't clear the rep_ pointer. It's the responsibility of the code that
192 // deletes rep_ to either clear the rep_ pointer or delete this slot_base.
193 if (!notifier.deleted_)
195 rep_->remove_destroy_notify_callback(¬ifier);
196 delete rep_; // Detach the stored functor from the other referred trackables and destroy it.
201 slot_base& slot_base::operator=(const slot_base& src)
203 if (src.rep_ == rep_)
205 blocked_ = src.blocked_;
211 delete_rep_with_check();
216 auto new_rep_ = src.rep_->dup();
218 if (rep_) // Silently exchange the slot_rep.
220 new_rep_->set_parent(rep_->parent_, rep_->cleanup_);
221 delete rep_; // Calls destroy(), but does not call disconnect().
225 blocked_ = src.blocked_;
230 slot_base& slot_base::operator=(slot_base&& src)
232 if (src.rep_ == rep_)
234 blocked_ = src.blocked_;
240 delete_rep_with_check();
244 blocked_ = src.blocked_;
245 internal::slot_rep* new_rep_ = nullptr;
246 if (src.rep_->parent_)
248 // src is connected to a parent, e.g. a sigc::signal.
249 // Copy, don't move! See https://bugzilla.gnome.org/show_bug.cgi?id=756484
250 new_rep_ = src.rep_->dup();
254 // src is not connected. Really move src.rep_.
255 src.rep_->notify_callbacks();
260 src.blocked_ = false;
263 if (rep_) // Silently exchange the slot_rep.
265 new_rep_->set_parent(rep_->parent_, rep_->cleanup_);
266 delete rep_; // Calls destroy(), but does not call disconnect().
272 void slot_base::set_parent(void* parent, void* (*cleanup)(void*)) const noexcept
275 rep_ = new dummy_slot_rep();
276 rep_->set_parent(parent, cleanup);
279 void slot_base::add_destroy_notify_callback(void* data, func_destroy_notify func) const
282 rep_->add_destroy_notify_callback(data, func);
285 void slot_base::remove_destroy_notify_callback(void* data) const
288 rep_->remove_destroy_notify_callback(data);
291 bool slot_base::block(bool should_block) noexcept
294 blocked_ = should_block;
298 bool slot_base::unblock() noexcept
303 void slot_base::disconnect()
310 /*bool slot_base::empty() const // having this function not inline is killing performance !!!
312 if (rep_ && !rep_->call_)
314 delete rep_; // This is not strictly necessary here. I'm convinced that it is
315 rep_ = nullptr; // safe to wait for the destructor to delete the slot_rep. Martin.
317 return (rep_ == nullptr);