Skip to content

Commit 404a79c

Browse files
author
Kjell Ahlstedt
committed
signal_impl: Use std::weak_ptr<signal_impl> in connected slots
A signal_impl object shall not store std::shared_ptr to itself via connected slots. It results in memory leaks. Use std::weak_ptr in the self_and_iter struct. Bug 775871
1 parent abf29c4 commit 404a79c

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

sigc++/signal_base.cc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ namespace internal
2828
// when the slot is disconnected. Bug 167714.
2929
struct self_and_iter : public notifiable
3030
{
31-
const std::shared_ptr<signal_impl> self_;
31+
const std::weak_ptr<signal_impl> self_;
3232
const signal_impl::iterator_type iter_;
3333

34-
self_and_iter(const std::shared_ptr<signal_impl>& self, const signal_impl::iterator_type& iter) : self_(self), iter_(iter) {}
34+
self_and_iter(const std::weak_ptr<signal_impl>& self, const signal_impl::iterator_type& iter)
35+
: self_(self), iter_(iter) {}
3536
};
3637

3738
signal_impl::signal_impl() : exec_count_(0), deferred_(false)
@@ -64,6 +65,9 @@ signal_impl::clear()
6465
{
6566
// Don't let signal_impl::notify() erase the slots. It would invalidate the
6667
// iterator in the following loop.
68+
// Don't call shared_from_this() here. clear() is called from the destructor.
69+
// When the destructor is executing, shared_ptr's use_count has reached 0,
70+
// and it's no longer possible to get a shared_ptr to this.
6771
const bool saved_deferred = deferred_;
6872
signal_impl_exec_holder exec(this);
6973

@@ -179,22 +183,30 @@ void
179183
signal_impl::notify_self_and_iter_of_invalidated_slot(notifiable* d)
180184
{
181185
std::unique_ptr<self_and_iter> si(static_cast<self_and_iter*>(d));
186+
auto self = si->self_.lock();
187+
if (!self)
188+
{
189+
// The signal_impl object is being deleted. The use_count has reached 0.
190+
// Nothing to do here. exec_count_ > 0 and clear() will restore deferred_.
191+
return;
192+
}
182193

183-
if (si->self_->exec_count_ == 0)
194+
if (self->exec_count_ == 0)
184195
{
185196
// The deletion of a slot may cause the deletion of a signal_base,
186197
// a decrementation of si->self_->ref_count_, and the deletion of si->self_.
187198
// In that case, the deletion of si->self_ is deferred to ~signal_impl_holder().
188-
signal_impl_holder exec(si->self_);
189-
si->self_->slots_.erase(si->iter_);
199+
// https://bugzilla.gnome.org/show_bug.cgi?id=564005#c24
200+
signal_impl_holder exec(self);
201+
self->slots_.erase(si->iter_);
190202
}
191203
else
192204
{
193205
// This is occurring during signal emission or slot erasure.
194206
// => sweep() will be called from ~signal_impl_holder() after signal emission.
195207
// This is safer because we don't have to care about our
196208
// iterators in emit(), clear(), and erase().
197-
si->self_->deferred_ = true;
209+
self->deferred_ = true;
198210
}
199211
}
200212

0 commit comments

Comments
 (0)