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:
Vadim Zeitlin
2009-07-21 14:16:44 +00:00
parent 2388960a08
commit 3185abc278
8 changed files with 203 additions and 129 deletions

View File

@@ -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();

View File

@@ -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__

View File

@@ -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

View File

@@ -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

View File

@@ -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(),
@@ -470,6 +478,10 @@ void wxAppConsoleBase::ProcessPendingEvents()
}
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
// Garbage collect all objects previously scheduled for destruction.
DeletePendingObjects();
}
void wxAppConsoleBase::DeletePendingEvents()
@@ -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
// ----------------------------------------------------------------------------

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;