Fix crashes after using "wildcard" wxEvtHandler::Disconnect().

When not specifying the function to disconnect, the associated event sink was
destroyed too early resulting in crashes later. Fix this and add unit tests
verifying that things work as expected and at least don't crash.

Closes #14563.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72943 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-11-10 12:22:32 +00:00
parent 4677003143
commit 75b2220eba
2 changed files with 38 additions and 9 deletions

View File

@@ -1675,15 +1675,6 @@ wxEvtHandler::DoUnbind(int id,
if (!m_dynamicEvents)
return false;
// Remove connection from tracker node (wxEventConnectionRef)
wxEvtHandler *eventSink = func.GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
if ( evtConnRef )
evtConnRef->DecRef();
}
wxList::compatibility_iterator node = m_dynamicEvents->GetFirst();
while (node)
{
@@ -1695,6 +1686,15 @@ wxEvtHandler::DoUnbind(int id,
entry->m_fn->IsMatching(func) &&
((entry->m_callbackUserData == userData) || !userData))
{
// Remove connection from tracker node (wxEventConnectionRef)
wxEvtHandler *eventSink = entry->m_fn->GetEvtHandler();
if ( eventSink && eventSink != this )
{
wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
if ( evtConnRef )
evtConnRef->DecRef();
}
delete entry->m_callbackUserData;
m_dynamicEvents->Erase( node );
delete entry;

View File

@@ -160,6 +160,8 @@ private:
CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
CPPUNIT_TEST( BuiltinConnect );
CPPUNIT_TEST( LegacyConnect );
CPPUNIT_TEST( DisconnectWildcard );
CPPUNIT_TEST( AutoDisconnect );
#ifdef wxHAS_EVENT_BIND
CPPUNIT_TEST( BindFunction );
CPPUNIT_TEST( BindStaticMethod );
@@ -174,6 +176,8 @@ private:
void BuiltinConnect();
void LegacyConnect();
void DisconnectWildcard();
void AutoDisconnect();
#ifdef wxHAS_EVENT_BIND
void BindFunction();
void BindStaticMethod();
@@ -249,6 +253,31 @@ void EvtHandlerTestCase::LegacyConnect()
handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
}
void EvtHandlerTestCase::DisconnectWildcard()
{
// should be able to disconnect a different handler using "wildcard search"
MyHandler sink;
wxEvtHandler source;
source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
CPPUNIT_ASSERT(source.Disconnect(wxID_ANY, wxEVT_IDLE));
// destruction of source and sink here should properly clean up the
// wxEventConnectionRef without crashing
}
void EvtHandlerTestCase::AutoDisconnect()
{
wxEvtHandler source;
{
MyHandler sink;
source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink);
// mismatched event type, so nothing should be disconnected
CPPUNIT_ASSERT(!source.Disconnect(wxEVT_THREAD, wxIdleEventHandler(MyHandler::OnIdle), NULL, &sink));
}
// destruction of sink should have automatically disconnected it, so
// there should be nothing to disconnect anymore
CPPUNIT_ASSERT(!source.Disconnect(wxID_ANY, wxEVT_IDLE));
}
#ifdef wxHAS_EVENT_BIND
void EvtHandlerTestCase::BindFunction()