I've turned up a rather obscure way to cause memory corruption using
libsigc++. (the attached program crashes, but I originally only found the
bug through valgrind) Here's a short description:
(1) Create a signal
(2) Attach two callbacks holding references to the same trackable
instance to this signal. One of these callbacks should bind a
value whose deletion triggers the clearing or deletion of the
signal.
(3) Delete the instance.
What happens at step (3) is roughly:
(a) The first attached callback is deleted from the signal. The bound value
is deleted, causing the signal to be cleared (all callbacks attached to
it are deleted).
(b) The second attached callback is deleted......oops. If executed before
(a), this simply seems to crash the program; otherwise it double-frees.
Note that I am NOT deleting a signal while it's being emitted (which
obviously is not a good idea); the problem occurs when you delete an object
that has several callbacks connected to a single signal.
On the one hand, I can see this being a weird corner case and maybe even an
abuse of the library; on the other hand, if you have values that represent
counted references to objects (i.e., deleting them decrements a reference
count), binding them as signal arguments seems like a logical way of ensuring
that they don't die before the connection -- assuming, of course, that you
avoid creating cycles.
I've attached a stripped-down example case that illustrates what I'm talking
about. If this is an expected problem, I guess I can design around it.
Daniel
--
/------------------- Daniel Burrows <dburrows debian org> ------------------\
| Corporation: An ingenious device for obtaining individual profit |
| without individual responsibility. |
| -- Ambrose Bierce (1842-1914), "The Devil's Dictionary", 1911 |
\---------------- The Turtle Moves! -- http://www.lspace.org ---------------/
#include <sigc++/signal.h>
#include <sigc++/adaptors/bind.h>
#include <sigc++/functors/mem_fun.h>
#include <sigc++/trackable.h>
#include <iostream>
using namespace std;
class sillier;
class silly : virtual public sigc::trackable
{
int a;
public:
silly(int aInit) : a(aInit)
{
}
void blah(sillier &)
{
}
void foo()
{
cout << a << endl;
}
};
class sillier
{
sigc::signal0<void> &s;
public:
sillier(sigc::signal0<void> &sInit) : s(sInit)
{
}
~sillier()
{
s.clear();
}
};
int main(int argc, char **argv)
{
sigc::signal0<void> some_signal;
silly * s = new silly(5);
sillier u(some_signal);
some_signal.connect(sigc::mem_fun(*s, &silly::foo));
some_signal.connect(sigc::bind(sigc::mem_fun(*s, &silly::blah),
u));
some_signal();
delete s;
return 0;
}
Attachment:
pgpUgxs8WFeNS.pgp
Description: PGP signature