Fix crash when unbinding event handlers from other handlers
Calling Unbind() on another handler from the currently executing handler which had been bound after (and hence executed before) the handler being unbound resulted in a crash previously as the iterators used in the loop over all dynamic event handlers became invalid. Fix this by storing the dynamic event table entries in a vector instead of a list (which is also more memory and speed efficient anyhow) and null the deleted entries instead of removing them to avoid invalidating the iterators and only really remove them once we finish iterating. Closes #17229.
This commit is contained in:
@@ -164,6 +164,7 @@ private:
|
||||
CPPUNIT_TEST( BindFunctionUsingBaseEvent );
|
||||
CPPUNIT_TEST( BindNonHandler );
|
||||
CPPUNIT_TEST( InvalidBind );
|
||||
CPPUNIT_TEST( UnbindFromHandler );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void BuiltinConnect();
|
||||
@@ -178,6 +179,7 @@ private:
|
||||
void BindFunctionUsingBaseEvent();
|
||||
void BindNonHandler();
|
||||
void InvalidBind();
|
||||
void UnbindFromHandler();
|
||||
|
||||
|
||||
// these member variables exceptionally don't use "m_" prefix because
|
||||
@@ -459,3 +461,48 @@ void EvtHandlerTestCase::InvalidBind()
|
||||
myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EvtHandlerTestCase::UnbindFromHandler()
|
||||
{
|
||||
struct Handler1
|
||||
{
|
||||
void OnDontCall(MyEvent&)
|
||||
{
|
||||
// Although this handler is bound, the second one below is bound
|
||||
// later and so will be called first and will disconnect this one
|
||||
// before it has a chance to be called.
|
||||
CPPUNIT_FAIL("shouldn't be called");
|
||||
}
|
||||
} h1;
|
||||
|
||||
handler.Bind(MyEventType, &Handler1::OnDontCall, &h1);
|
||||
|
||||
class Handler2
|
||||
{
|
||||
public:
|
||||
Handler2(MyHandler& handler, Handler1& h1)
|
||||
{
|
||||
m_handler = &handler;
|
||||
m_h1 = &h1;
|
||||
}
|
||||
|
||||
void OnUnbind(MyEvent& e)
|
||||
{
|
||||
m_handler->Unbind(MyEventType, &Handler1::OnDontCall, m_h1);
|
||||
|
||||
// Check that the now disconnected first handler is not executed.
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
private:
|
||||
// Use pointers instead of references just to avoid warnings about the
|
||||
// class being non-copyable even though these pointers don't change and
|
||||
// are non-NULL.
|
||||
MyHandler* m_handler;
|
||||
Handler1* m_h1;
|
||||
} h2(handler, h1);
|
||||
|
||||
handler.Bind(MyEventType, &Handler2::OnUnbind, &h2);
|
||||
|
||||
handler.ProcessEvent(e);
|
||||
}
|
||||
|
Reference in New Issue
Block a user