]> git.tdb.fi Git - ext/sigc++-2.0.git/blob - sigc++/functors/slot_base.cc
Import libsigc++ 2.10.8 sources
[ext/sigc++-2.0.git] / sigc++ / functors / slot_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  */
19
20 #include <sigc++/functors/slot_base.h>
21
22 namespace
23 {
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
27 {
28   destroy_notify_struct() noexcept : deleted_(false) { }
29
30   static void* notify(void* data) noexcept
31   {
32     auto self_ = reinterpret_cast<destroy_notify_struct*>(data);
33     self_->deleted_ = true;
34     return nullptr;
35   }
36
37   bool deleted_;
38 };
39
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
42 {
43 public:
44   dummy_slot_rep() : slot_rep(nullptr, nullptr, &clone) {}
45   static void* clone(void*) { return new dummy_slot_rep(); }
46 };
47 } // anonymous namespace
48
49 namespace sigc
50 {
51 namespace internal
52 {
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_)
56 {
57   return malloc(size_);
58 }
59
60 void slot_rep::operator delete(void* p)
61 {
62   free(p);
63 }
64 #endif
65
66 void slot_rep::disconnect()
67 {
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
74   call_ = nullptr;
75
76   if (parent_)
77   {
78     auto data_ = parent_;
79     parent_ = nullptr;  // Just a precaution.
80     (cleanup_)(data_);  // Notify the parent (might lead to destruction of this!).
81   }
82 }
83
84 //static
85 void* slot_rep::notify(void* data)
86 {
87   auto self_ = reinterpret_cast<slot_rep*>(data);
88
89   self_->call_ = nullptr; // Invalidate the slot.
90   
91   // Make sure we are notified if disconnect() deletes self_, which is trackable.
92   destroy_notify_struct notifier;
93   self_->add_destroy_notify_callback(&notifier, 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_)
97   {
98     self_->remove_destroy_notify_callback(&notifier);
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.
101   }
102   return nullptr;
103 }
104
105 } // namespace internal
106   
107 slot_base::slot_base() noexcept
108 : rep_(nullptr),
109   blocked_(false)
110 {}
111
112 slot_base::slot_base(rep_type* rep) noexcept
113 : rep_(rep),
114   blocked_(false)
115 {}
116
117 slot_base::slot_base(const slot_base& src)
118 : rep_(nullptr),
119   blocked_(src.blocked_)
120 {
121   if (src.rep_)
122   {
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.
126     if (src.rep_->call_)
127       rep_ = src.rep_->dup();
128     else
129     {
130       *this = slot_base(); //Return the default invalid slot.
131     }
132   }
133 }
134
135 slot_base::slot_base(slot_base&& src)
136 : rep_(nullptr),
137   blocked_(src.blocked_)
138 {
139   if (src.rep_)
140   {
141     if (src.rep_->parent_)
142     {
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
145
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().
149       if (src.rep_->call_)
150         rep_ = src.rep_->dup();
151       else
152         blocked_ = false; //Return the default invalid slot.
153     }
154     else
155     {
156       // src is not connected. Really move src.rep_.
157       src.rep_->notify_callbacks();
158       rep_ = src.rep_;
159
160       //Wipe src:
161       src.rep_ = nullptr;
162       src.blocked_ = false;
163     }
164   }
165 }
166
167 slot_base::~slot_base()
168 {
169   if (rep_)
170     delete rep_;
171 }
172
173 slot_base::operator bool() const noexcept
174 {
175   return rep_ != nullptr;
176 }
177
178 void slot_base::delete_rep_with_check()
179 {
180   if (!rep_)
181     return;
182
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(&notifier, destroy_notify_struct::notify);
187   rep_->disconnect(); // Disconnect the slot (might lead to deletion of rep_!).
188
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_)
194   {
195     rep_->remove_destroy_notify_callback(&notifier);
196     delete rep_; // Detach the stored functor from the other referred trackables and destroy it.
197     rep_ = nullptr;
198   }
199 }
200
201 slot_base& slot_base::operator=(const slot_base& src)
202 {
203   if (src.rep_ == rep_)
204   {
205     blocked_ = src.blocked_;
206     return *this;
207   }
208
209   if (src.empty())
210   {
211     delete_rep_with_check();
212
213     return *this;
214   }
215
216   auto new_rep_ = src.rep_->dup();
217
218   if (rep_) // Silently exchange the slot_rep.
219   {
220     new_rep_->set_parent(rep_->parent_, rep_->cleanup_);
221     delete rep_; // Calls destroy(), but does not call disconnect().
222   }
223
224   rep_ = new_rep_;
225   blocked_ = src.blocked_;
226
227   return *this;
228 }
229
230 slot_base& slot_base::operator=(slot_base&& src)
231 {
232   if (src.rep_ == rep_)
233   {
234     blocked_ = src.blocked_;
235     return *this;
236   }
237
238   if (src.empty())
239   {
240     delete_rep_with_check();
241     return *this;
242   }
243
244   blocked_ = src.blocked_;
245   internal::slot_rep* new_rep_ = nullptr;
246   if (src.rep_->parent_)
247   {
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();
251   }
252   else
253   {
254     // src is not connected. Really move src.rep_.
255     src.rep_->notify_callbacks();
256     new_rep_ = src.rep_;
257
258     //Wipe src:
259     src.rep_ = nullptr;
260     src.blocked_ = false;
261   }
262
263   if (rep_) // Silently exchange the slot_rep.
264   {
265     new_rep_->set_parent(rep_->parent_, rep_->cleanup_);
266     delete rep_; // Calls destroy(), but does not call disconnect().
267   }
268   rep_ = new_rep_;
269   return *this;
270 }
271
272 void slot_base::set_parent(void* parent, void* (*cleanup)(void*)) const noexcept
273 {
274   if (!rep_)
275     rep_ = new dummy_slot_rep();
276   rep_->set_parent(parent, cleanup);
277 }
278
279 void slot_base::add_destroy_notify_callback(void* data, func_destroy_notify func) const
280 {
281   if (rep_)
282     rep_->add_destroy_notify_callback(data, func);
283 }
284
285 void slot_base::remove_destroy_notify_callback(void* data) const
286 {
287   if (rep_)
288     rep_->remove_destroy_notify_callback(data);
289 }
290
291 bool slot_base::block(bool should_block) noexcept
292 {
293   bool old = blocked_;
294   blocked_ = should_block;
295   return old;
296 }
297
298 bool slot_base::unblock() noexcept
299 {
300   return block(false);
301 }
302
303 void slot_base::disconnect()
304 {
305   if (rep_)
306     rep_->disconnect();
307 }
308
309
310 /*bool slot_base::empty() const // having this function not inline is killing performance !!!
311 {
312   if (rep_ && !rep_->call_)
313     {
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.
316     }
317   return (rep_ == nullptr);
318 }*/
319
320 } //namespace sigc