Support delayed destruction in console applications too.\n\nThis only works if there is a running event loop but if there is one, we can have the same kind of problems with non-GUI objects such as sockets in console applications as we have with windows in GUI ones, so we must support this (see #10989).
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61488 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -52,6 +52,15 @@ enum
|
||||
wxPRINT_POSTSCRIPT = 2
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// global variables
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// use of this list is strongly deprecated, use wxApp ScheduleForDestruction()
|
||||
// and IsScheduledForDestruction() methods instead of this list directly, it
|
||||
// is here for compatibility purposes only
|
||||
extern WXDLLIMPEXP_DATA_BASE(wxList) wxPendingDelete;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxAppConsoleBase: wxApp for non-GUI applications
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -317,8 +326,27 @@ public:
|
||||
void DeletePendingEvents();
|
||||
|
||||
|
||||
// wxEventLoop redirections
|
||||
// ------------------------
|
||||
// delayed destruction
|
||||
// -------------------
|
||||
|
||||
// If an object may have pending events for it, it shouldn't be deleted
|
||||
// immediately as this would result in a crash when trying to handle these
|
||||
// events: instead, it should be scheduled for destruction and really
|
||||
// destroyed only after processing all pending events.
|
||||
//
|
||||
// Notice that this is only possible if we have a running event loop,
|
||||
// otherwise the object is just deleted directly by ScheduleForDestruction()
|
||||
// and IsScheduledForDestruction() always returns false.
|
||||
|
||||
// schedule the object for destruction in the near future
|
||||
void ScheduleForDestruction(wxObject *object);
|
||||
|
||||
// return true if the object is scheduled for destruction
|
||||
bool IsScheduledForDestruction(wxObject *object) const;
|
||||
|
||||
|
||||
// wxEventLoop-related methods
|
||||
// ---------------------------
|
||||
|
||||
// all these functions are forwarded to the corresponding methods of the
|
||||
// currently active event loop -- and do nothing if there is none
|
||||
@@ -340,6 +368,12 @@ public:
|
||||
// events are still sent out
|
||||
virtual bool ProcessIdle();
|
||||
|
||||
// this virtual function is overridden in GUI wxApp to always return true
|
||||
// as GUI applications always have an event loop -- but console ones may
|
||||
// have it or not, so it simply returns true if already have an event loop
|
||||
// running but false otherwise
|
||||
virtual bool UsesEventLoop() const;
|
||||
|
||||
|
||||
// debugging support
|
||||
// -----------------
|
||||
@@ -401,6 +435,11 @@ public:
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// delete all objects in wxPendingDelete list
|
||||
//
|
||||
// called from ProcessPendingEvents()
|
||||
void DeletePendingObjects();
|
||||
|
||||
// the function which creates the traits object when GetTraits() needs it
|
||||
// for the first time
|
||||
virtual wxAppTraits *CreateTraits();
|
||||
@@ -527,6 +566,9 @@ public:
|
||||
// Returns true if more idle time is requested.
|
||||
virtual bool SendIdleEvents(wxWindow* win, wxIdleEvent& event);
|
||||
|
||||
// override base class version: GUI apps always use an event loop
|
||||
virtual bool UsesEventLoop() const { return true; }
|
||||
|
||||
|
||||
// top level window functions
|
||||
// --------------------------
|
||||
@@ -608,9 +650,6 @@ public:
|
||||
#endif // WXWIN_COMPATIBILITY_2_6
|
||||
|
||||
protected:
|
||||
// delete all objects in wxPendingDelete list
|
||||
void DeletePendingObjects();
|
||||
|
||||
// override base class method to use GUI traits
|
||||
virtual wxAppTraits *CreateTraits();
|
||||
|
||||
|
@@ -99,20 +99,6 @@ public:
|
||||
// return true if fprintf(stderr) goes somewhere, false otherwise
|
||||
virtual bool HasStderr() = 0;
|
||||
|
||||
// managing "pending delete" list: in GUI mode we can't immediately delete
|
||||
// some objects because there may be unprocessed events for them and so we
|
||||
// only do it during the next idle loop iteration while this is, of course,
|
||||
// unnecessary in wxBase, so we have a few functions to abstract these
|
||||
// operations
|
||||
|
||||
// add the object to the pending delete list in GUI, delete it immediately
|
||||
// in wxBase
|
||||
virtual void ScheduleForDestroy(wxObject *object) = 0;
|
||||
|
||||
// remove this object from the pending delete list in GUI, do nothing in
|
||||
// wxBase
|
||||
virtual void RemoveFromPendingDelete(wxObject *object) = 0;
|
||||
|
||||
#if wxUSE_SOCKETS
|
||||
// this function is used by wxNet library to set the default socket manager
|
||||
// to use: doing it like this allows us to keep all socket-related code in
|
||||
@@ -233,9 +219,6 @@ public:
|
||||
virtual bool ShowAssertDialog(const wxString& msg);
|
||||
virtual bool HasStderr();
|
||||
|
||||
virtual void ScheduleForDestroy(wxObject *object);
|
||||
virtual void RemoveFromPendingDelete(wxObject *object);
|
||||
|
||||
// the GetToolkitVersion for console application is always the same
|
||||
virtual wxPortId GetToolkitVersion(int *verMaj = NULL, int *verMin = NULL) const
|
||||
{
|
||||
@@ -272,9 +255,6 @@ public:
|
||||
virtual bool ShowAssertDialog(const wxString& msg);
|
||||
virtual bool HasStderr();
|
||||
|
||||
virtual void ScheduleForDestroy(wxObject *object);
|
||||
virtual void RemoveFromPendingDelete(wxObject *object);
|
||||
|
||||
virtual bool IsUsingUniversalWidgets() const
|
||||
{
|
||||
#ifdef __WXUNIVERSAL__
|
||||
|
@@ -148,7 +148,9 @@ WX_DECLARE_LIST_3(wxWindow, wxWindowBase, wxWindowList, wxWindowListNode, class
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
extern WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows;
|
||||
extern WXDLLIMPEXP_DATA_CORE(wxList) wxPendingDelete;
|
||||
|
||||
// declared here for compatibility only, main declaration is in wx/app.h
|
||||
extern WXDLLIMPEXP_DATA_BASE(wxList) wxPendingDelete;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxWindowBase is the base class for all GUI controls/widgets, this is the public
|
||||
|
@@ -111,6 +111,20 @@ public:
|
||||
wxEventFunction func,
|
||||
wxEvent& event) const;
|
||||
|
||||
/**
|
||||
Returns @true if the application is using an event loop.
|
||||
|
||||
This function always returns @true for the GUI applications which
|
||||
must use an event loop but by default only returns @true for the
|
||||
console programs if an event loop is already running as it can't know
|
||||
whether one will be created in the future.
|
||||
|
||||
Thus, it only makes sense to override it in console applications which
|
||||
do use an event loop, to return @true instead of checking if there is a
|
||||
currently active event loop.
|
||||
*/
|
||||
virtual bool UsesEventLoop() const;
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
@@ -172,6 +186,40 @@ public:
|
||||
|
||||
//@}
|
||||
|
||||
/**
|
||||
Delayed objects destruction.
|
||||
|
||||
In applications using events it may be unsafe for an event handler to
|
||||
delete the object which generated the event because more events may be
|
||||
still pending for the same object. In this case the handler may call
|
||||
ScheduleForDestruction() instead.
|
||||
*/
|
||||
//@{
|
||||
|
||||
/**
|
||||
Schedule the object for destruction in the near future.
|
||||
|
||||
Notice that if the application is not using an event loop, i.e. if
|
||||
UsesEventLoop() returns @false, this method will simply delete the
|
||||
object immediately.
|
||||
|
||||
Examples of using this function inside wxWidgets itself include
|
||||
deleting the top level windows when they are closed and sockets when
|
||||
they are disconnected.
|
||||
*/
|
||||
void ScheduleForDestruction(wxObject *object);
|
||||
|
||||
/**
|
||||
Check if the object had been scheduled for destruction with
|
||||
ScheduleForDestruction().
|
||||
|
||||
This function may be useful as an optimization to avoid doing something
|
||||
with an object which will be soon destroyed in any case.
|
||||
*/
|
||||
bool IsScheduledForDestruction(wxObject *object) const;
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/**
|
||||
Allows external code to modify global ::wxTheApp, but you should really
|
||||
|
@@ -115,6 +115,8 @@ wxAppInitializerFunction wxAppConsoleBase::ms_appInitFn = NULL;
|
||||
|
||||
wxSocketManager *wxAppTraitsBase::ms_manager = NULL;
|
||||
|
||||
WXDLLIMPEXP_DATA_BASE(wxList) wxPendingDelete;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxEventLoopPtr
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -341,6 +343,13 @@ bool wxAppConsoleBase::ProcessIdle()
|
||||
return event.MoreRequested();
|
||||
}
|
||||
|
||||
bool wxAppConsoleBase::UsesEventLoop() const
|
||||
{
|
||||
// in console applications we don't know whether we're going to have an
|
||||
// event loop so assume we won't -- unless we already have one running
|
||||
return wxEventLoopBase::GetActive() != NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// events
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -433,9 +442,8 @@ void wxAppConsoleBase::ResumeProcessingOfPendingEvents()
|
||||
|
||||
void wxAppConsoleBase::ProcessPendingEvents()
|
||||
{
|
||||
if (!m_bDoPendingEventProcessing)
|
||||
return;
|
||||
|
||||
if ( m_bDoPendingEventProcessing )
|
||||
{
|
||||
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
|
||||
|
||||
wxCHECK_RET( m_handlersWithPendingDelayedEvents.IsEmpty(),
|
||||
@@ -472,6 +480,10 @@ void wxAppConsoleBase::ProcessPendingEvents()
|
||||
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
|
||||
}
|
||||
|
||||
// Garbage collect all objects previously scheduled for destruction.
|
||||
DeletePendingObjects();
|
||||
}
|
||||
|
||||
void wxAppConsoleBase::DeletePendingEvents()
|
||||
{
|
||||
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
|
||||
@@ -487,6 +499,50 @@ void wxAppConsoleBase::DeletePendingEvents()
|
||||
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// delayed objects destruction
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
bool wxAppConsoleBase::IsScheduledForDestruction(wxObject *object) const
|
||||
{
|
||||
return wxPendingDelete.Member(object);
|
||||
}
|
||||
|
||||
void wxAppConsoleBase::ScheduleForDestruction(wxObject *object)
|
||||
{
|
||||
if ( !UsesEventLoop() )
|
||||
{
|
||||
// we won't be able to delete it later so do it right now
|
||||
delete object;
|
||||
return;
|
||||
}
|
||||
//else: we either already have or will soon start an event loop
|
||||
|
||||
if ( !wxPendingDelete.Member(object) )
|
||||
wxPendingDelete.Append(object);
|
||||
}
|
||||
|
||||
void wxAppConsoleBase::DeletePendingObjects()
|
||||
{
|
||||
wxList::compatibility_iterator node = wxPendingDelete.GetFirst();
|
||||
while (node)
|
||||
{
|
||||
wxObject *obj = node->GetData();
|
||||
|
||||
// remove it from the list first so that if we get back here somehow
|
||||
// during the object deletion (e.g. wxYield called from its dtor) we
|
||||
// wouldn't try to delete it the second time
|
||||
if ( wxPendingDelete.Member(obj) )
|
||||
wxPendingDelete.Erase(node);
|
||||
|
||||
delete obj;
|
||||
|
||||
// Deleting one object may have deleted other pending
|
||||
// objects, so start from beginning of list again.
|
||||
node = wxPendingDelete.GetFirst();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// exception handling
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -738,16 +794,6 @@ bool wxConsoleAppTraitsBase::HasStderr()
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxConsoleAppTraitsBase::ScheduleForDestroy(wxObject *object)
|
||||
{
|
||||
delete object;
|
||||
}
|
||||
|
||||
void wxConsoleAppTraitsBase::RemoveFromPendingDelete(wxObject * WXUNUSED(object))
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxAppTraits
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@@ -50,8 +50,6 @@
|
||||
#include "wx/build.h"
|
||||
WX_CHECK_BUILD_OPTIONS("wxCore")
|
||||
|
||||
WXDLLIMPEXP_DATA_CORE(wxList) wxPendingDelete;
|
||||
|
||||
// ============================================================================
|
||||
// wxAppBase implementation
|
||||
// ============================================================================
|
||||
@@ -341,33 +339,11 @@ bool wxAppBase::SafeYieldFor(wxWindow *win, long eventsToProcess)
|
||||
// idle handling
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void wxAppBase::DeletePendingObjects()
|
||||
{
|
||||
wxList::compatibility_iterator node = wxPendingDelete.GetFirst();
|
||||
while (node)
|
||||
{
|
||||
wxObject *obj = node->GetData();
|
||||
|
||||
// remove it from the list first so that if we get back here somehow
|
||||
// during the object deletion (e.g. wxYield called from its dtor) we
|
||||
// wouldn't try to delete it the second time
|
||||
if ( wxPendingDelete.Member(obj) )
|
||||
wxPendingDelete.Erase(node);
|
||||
|
||||
delete obj;
|
||||
|
||||
// Deleting one object may have deleted other pending
|
||||
// objects, so start from beginning of list again.
|
||||
node = wxPendingDelete.GetFirst();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if more time is needed.
|
||||
bool wxAppBase::ProcessIdle()
|
||||
{
|
||||
// call the base class version first, it will process the pending events
|
||||
// (which should be done before the idle events generation) and send the
|
||||
// idle event to wxTheApp itself
|
||||
// call the base class version first to send the idle event to wxTheApp
|
||||
// itself
|
||||
bool needMore = wxAppConsoleBase::ProcessIdle();
|
||||
wxIdleEvent event;
|
||||
wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
|
||||
@@ -379,9 +355,6 @@ bool wxAppBase::ProcessIdle()
|
||||
node = node->GetNext();
|
||||
}
|
||||
|
||||
// 'Garbage' collection of windows deleted with Close().
|
||||
DeletePendingObjects();
|
||||
|
||||
#if wxUSE_LOG
|
||||
// flush the logged messages if any
|
||||
wxLog::FlushActive();
|
||||
@@ -544,14 +517,3 @@ bool wxGUIAppTraitsBase::HasStderr()
|
||||
#endif
|
||||
}
|
||||
|
||||
void wxGUIAppTraitsBase::ScheduleForDestroy(wxObject *object)
|
||||
{
|
||||
if ( !wxPendingDelete.Member(object) )
|
||||
wxPendingDelete.Append(object);
|
||||
}
|
||||
|
||||
void wxGUIAppTraitsBase::RemoveFromPendingDelete(wxObject *object)
|
||||
{
|
||||
wxPendingDelete.DeleteObject(object);
|
||||
}
|
||||
|
||||
|
@@ -695,6 +695,11 @@ void wxTCPEventHandler::HandleDisconnect(wxTCPConnection *connection)
|
||||
connection->m_sock->Notify(false);
|
||||
connection->m_sock->Close();
|
||||
|
||||
// don't leave references to this soon-to-be-dangling connection in the
|
||||
// socket as it won't be destroyed immediately as its destruction will be
|
||||
// delayed in case there are more events pending for it
|
||||
connection->m_sock->SetClientData(NULL);
|
||||
|
||||
connection->SetConnected(false);
|
||||
connection->OnDisconnect();
|
||||
}
|
||||
|
@@ -841,12 +841,6 @@ wxSocketBase::wxSocketBase(wxSocketFlags flags, wxSocketType type)
|
||||
|
||||
wxSocketBase::~wxSocketBase()
|
||||
{
|
||||
// Just in case the app called Destroy() *and* then deleted the socket
|
||||
// immediately: don't leave dangling pointers.
|
||||
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
|
||||
if ( traits )
|
||||
traits->RemoveFromPendingDelete(this);
|
||||
|
||||
// Shutdown and close the socket
|
||||
if (!m_beingDeleted)
|
||||
Close();
|
||||
@@ -855,7 +849,6 @@ wxSocketBase::~wxSocketBase()
|
||||
delete m_impl;
|
||||
|
||||
// Free the pushback buffer
|
||||
if (m_unread)
|
||||
free(m_unread);
|
||||
}
|
||||
|
||||
@@ -872,14 +865,13 @@ bool wxSocketBase::Destroy()
|
||||
// Suppress events from now on
|
||||
Notify(false);
|
||||
|
||||
// schedule this object for deletion
|
||||
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
|
||||
if ( traits )
|
||||
// Schedule this object for deletion instead of destroying it right now if
|
||||
// possible as we may have other events pending for it
|
||||
if ( wxTheApp )
|
||||
{
|
||||
// let the traits object decide what to do with us
|
||||
traits->ScheduleForDestroy(this);
|
||||
wxTheApp->ScheduleForDestruction(this);
|
||||
}
|
||||
else // no app or no traits
|
||||
else // no app
|
||||
{
|
||||
// in wxBase we might have no app object at all, don't leak memory
|
||||
delete this;
|
||||
|
Reference in New Issue
Block a user