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