Better handling for asserts in non-main threads.
Don't call wxTrap() when an assert in a non-main thread fails. As asserts are now always enabled by default, this is not a good idea. Instead just show the full details about the assert failure using wxMessageOutputDebug under the platforms without MT-safe message box function (i.e. everything but MSW currently). Add a possibility to test an assert happening in non-main thread to the except sample. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62842 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
5ceb93670c
commit
2d8e0096cd
@ -44,6 +44,8 @@
|
|||||||
#include "wx/utils.h"
|
#include "wx/utils.h"
|
||||||
#include "wx/msgdlg.h"
|
#include "wx/msgdlg.h"
|
||||||
#include "wx/icon.h"
|
#include "wx/icon.h"
|
||||||
|
|
||||||
|
#include "wx/thread.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -128,8 +130,13 @@ protected:
|
|||||||
// catch exceptions which occur in MyFrame methods here
|
// catch exceptions which occur in MyFrame methods here
|
||||||
virtual bool ProcessEvent(wxEvent& event);
|
virtual bool ProcessEvent(wxEvent& event);
|
||||||
|
|
||||||
// show how an assert failure message box looks like
|
// provoke assert in main or worker thread
|
||||||
|
//
|
||||||
|
// this is used to show how an assert failure message box looks like
|
||||||
void OnShowAssert(wxCommandEvent& event);
|
void OnShowAssert(wxCommandEvent& event);
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
void OnShowAssertInThread(wxCommandEvent& event);
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// any class wishing to process wxWidgets events must use this macro
|
// any class wishing to process wxWidgets events must use this macro
|
||||||
@ -185,6 +192,9 @@ enum
|
|||||||
Except_HandleCrash,
|
Except_HandleCrash,
|
||||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||||
Except_ShowAssert,
|
Except_ShowAssert,
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
Except_ShowAssertInThread,
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
Except_Dialog,
|
Except_Dialog,
|
||||||
|
|
||||||
Except_Quit = wxID_EXIT,
|
Except_Quit = wxID_EXIT,
|
||||||
@ -211,6 +221,9 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
|||||||
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
|
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
|
||||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||||
EVT_MENU(Except_ShowAssert, MyFrame::OnShowAssert)
|
EVT_MENU(Except_ShowAssert, MyFrame::OnShowAssert)
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
EVT_MENU(Except_ShowAssertInThread, MyFrame::OnShowAssertInThread)
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(MyDialog, wxDialog)
|
BEGIN_EVENT_TABLE(MyDialog, wxDialog)
|
||||||
@ -301,14 +314,17 @@ void MyApp::OnAssertFailure(const wxChar *file,
|
|||||||
const wxChar *cond,
|
const wxChar *cond,
|
||||||
const wxChar *msg)
|
const wxChar *msg)
|
||||||
{
|
{
|
||||||
if ( wxMessageBox
|
// take care to not show the message box from a worker thread, this doesn't
|
||||||
(
|
// work as it doesn't have any event loop
|
||||||
wxString::Format("An assert failed in %s().", func) +
|
if ( !wxIsMainThread() ||
|
||||||
"\n"
|
wxMessageBox
|
||||||
"Do you want to call the default assert handler?",
|
(
|
||||||
"wxExcept Sample",
|
wxString::Format("An assert failed in %s().", func) +
|
||||||
wxYES_NO | wxICON_QUESTION
|
"\n"
|
||||||
) == wxYES )
|
"Do you want to call the default assert handler?",
|
||||||
|
"wxExcept Sample",
|
||||||
|
wxYES_NO | wxICON_QUESTION
|
||||||
|
) == wxYES )
|
||||||
{
|
{
|
||||||
wxApp::OnAssertFailure(file, line, func, cond, msg);
|
wxApp::OnAssertFailure(file, line, func, cond, msg);
|
||||||
}
|
}
|
||||||
@ -343,6 +359,10 @@ MyFrame::MyFrame()
|
|||||||
menuFile->AppendSeparator();
|
menuFile->AppendSeparator();
|
||||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||||
menuFile->Append(Except_ShowAssert, wxT("Provoke &assert failure\tCtrl-A"));
|
menuFile->Append(Except_ShowAssert, wxT("Provoke &assert failure\tCtrl-A"));
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
menuFile->Append(Except_ShowAssertInThread,
|
||||||
|
wxT("Assert failure in &thread\tShift-Ctrl-A"));
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
menuFile->AppendSeparator();
|
menuFile->AppendSeparator();
|
||||||
menuFile->Append(Except_Quit, wxT("E&xit\tCtrl-Q"), wxT("Quit this program"));
|
menuFile->Append(Except_Quit, wxT("E&xit\tCtrl-Q"), wxT("Quit this program"));
|
||||||
|
|
||||||
@ -443,6 +463,35 @@ void MyFrame::OnShowAssert(wxCommandEvent& WXUNUSED(event))
|
|||||||
arr[0];
|
arr[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
|
||||||
|
void MyFrame::OnShowAssertInThread(wxCommandEvent& WXUNUSED(event))
|
||||||
|
{
|
||||||
|
class AssertThread : public wxThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AssertThread()
|
||||||
|
: wxThread(wxTHREAD_JOINABLE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void *Entry()
|
||||||
|
{
|
||||||
|
wxFAIL_MSG("Test assert in another thread.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AssertThread thread;
|
||||||
|
thread.Create();
|
||||||
|
thread.Run();
|
||||||
|
thread.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
|
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
wxString msg;
|
wxString msg;
|
||||||
|
@ -1186,11 +1186,9 @@ bool DoShowAssertDialog(const wxString& msg)
|
|||||||
//case IDNO: nothing to do
|
//case IDNO: nothing to do
|
||||||
}
|
}
|
||||||
#else // !__WXMSW__
|
#else // !__WXMSW__
|
||||||
wxFprintf(stderr, wxT("%s\n"), msg.c_str());
|
wxMessageOutputDebug().Output(msg);
|
||||||
fflush(stderr);
|
|
||||||
|
|
||||||
// TODO: ask the user to enter "Y" or "N" on the console?
|
// TODO: ask the user whether to trap on the console?
|
||||||
wxTrap();
|
|
||||||
#endif // __WXMSW__/!__WXMSW__
|
#endif // __WXMSW__/!__WXMSW__
|
||||||
|
|
||||||
// continue with the asserts
|
// continue with the asserts
|
||||||
@ -1236,27 +1234,15 @@ void ShowAssertDialog(const wxString& file,
|
|||||||
// since dialogs cannot be displayed
|
// since dialogs cannot be displayed
|
||||||
if ( !wxThread::IsMain() )
|
if ( !wxThread::IsMain() )
|
||||||
{
|
{
|
||||||
msg += wxT(" [in child thread]");
|
msg += wxString::Format(" [in thread %lx]", wxThread::GetCurrentId());
|
||||||
|
|
||||||
#if defined(__WXMSW__) && !defined(__WXMICROWIN__)
|
|
||||||
msg << wxT("\r\n");
|
|
||||||
OutputDebugString(msg.wx_str());
|
|
||||||
#else
|
|
||||||
// send to stderr
|
|
||||||
wxFprintf(stderr, wxT("%s\n"), msg.c_str());
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
// He-e-e-e-elp!! we're asserting in a child thread
|
|
||||||
wxTrap();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
#endif // wxUSE_THREADS
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
|
// log the assert in any case
|
||||||
|
wxMessageOutputDebug().Output(msg);
|
||||||
|
|
||||||
if ( !s_bNoAsserts )
|
if ( !s_bNoAsserts )
|
||||||
{
|
{
|
||||||
// send it to the normal log destination
|
|
||||||
wxLogDebug(wxT("%s"), msg.c_str());
|
|
||||||
|
|
||||||
if ( traits )
|
if ( traits )
|
||||||
{
|
{
|
||||||
// delegate showing assert dialog (if possible) to that class
|
// delegate showing assert dialog (if possible) to that class
|
||||||
|
@ -447,6 +447,7 @@ wxRendererNative *wxGUIAppTraitsBase::CreateRenderer()
|
|||||||
|
|
||||||
bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
|
bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
|
||||||
{
|
{
|
||||||
|
#if wxDEBUG_LEVEL
|
||||||
// under MSW we prefer to use the base class version using ::MessageBox()
|
// under MSW we prefer to use the base class version using ::MessageBox()
|
||||||
// even if wxMessageBox() is available because it has less chances to
|
// even if wxMessageBox() is available because it has less chances to
|
||||||
// double fault our app than our wxMessageBox()
|
// double fault our app than our wxMessageBox()
|
||||||
@ -455,50 +456,51 @@ bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
|
|||||||
//
|
//
|
||||||
// and finally we can't use wxMessageBox() if it wasn't compiled in, of
|
// and finally we can't use wxMessageBox() if it wasn't compiled in, of
|
||||||
// course
|
// course
|
||||||
#if defined(__WXMSW__) || defined(__WXDFB__) || !wxUSE_MSGDLG
|
#if !defined(__WXMSW__) && !defined(__WXDFB__) && wxUSE_MSGDLG
|
||||||
return wxAppTraitsBase::ShowAssertDialog(msg);
|
|
||||||
#else // wxUSE_MSGDLG
|
// we can't (safely) show the GUI dialog from another thread, only do it
|
||||||
#if wxDEBUG_LEVEL
|
// for the asserts in the main thread
|
||||||
wxString msgDlg = msg;
|
if ( wxIsMainThread() )
|
||||||
|
{
|
||||||
|
wxString msgDlg = msg;
|
||||||
|
|
||||||
#if wxUSE_STACKWALKER
|
#if wxUSE_STACKWALKER
|
||||||
// on Unix stack frame generation may take some time, depending on the
|
// on Unix stack frame generation may take some time, depending on the
|
||||||
// size of the executable mainly... warn the user that we are working
|
// size of the executable mainly... warn the user that we are working
|
||||||
wxFprintf(stderr, wxT("[Debug] Generating a stack trace... please wait"));
|
wxFprintf(stderr, wxT("[Debug] Generating a stack trace... please wait"));
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
const wxString stackTrace = GetAssertStackTrace();
|
const wxString stackTrace = GetAssertStackTrace();
|
||||||
if ( !stackTrace.empty() )
|
if ( !stackTrace.empty() )
|
||||||
msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
|
msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
|
||||||
#endif // wxUSE_STACKWALKER
|
#endif // wxUSE_STACKWALKER
|
||||||
|
|
||||||
// this message is intentionally not translated -- it is for
|
// this message is intentionally not translated -- it is for
|
||||||
// developpers only
|
// developpers only
|
||||||
msgDlg += wxT("\nDo you want to stop the program?\n")
|
msgDlg += wxT("\nDo you want to stop the program?\n")
|
||||||
wxT("You can also choose [Cancel] to suppress ")
|
wxT("You can also choose [Cancel] to suppress ")
|
||||||
wxT("further warnings.");
|
wxT("further warnings.");
|
||||||
|
|
||||||
switch ( wxMessageBox(msgDlg, wxT("wxWidgets Debug Alert"),
|
switch ( wxMessageBox(msgDlg, wxT("wxWidgets Debug Alert"),
|
||||||
wxYES_NO | wxCANCEL | wxICON_STOP ) )
|
wxYES_NO | wxCANCEL | wxICON_STOP ) )
|
||||||
{
|
{
|
||||||
case wxYES:
|
case wxYES:
|
||||||
wxTrap();
|
wxTrap();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxCANCEL:
|
case wxCANCEL:
|
||||||
// no more asserts
|
// no more asserts
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
//case wxNO: nothing to do
|
//case wxNO: nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#else // !wxDEBUG_LEVEL
|
#endif // wxUSE_MSGDLG
|
||||||
// this function always exists (for ABI compatibility) but is never called
|
#endif // wxDEBUG_LEVEL
|
||||||
// if debug level is 0 and so can simply do nothing then
|
|
||||||
wxUnusedVar(msg);
|
|
||||||
#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
|
|
||||||
|
|
||||||
return false;
|
return wxAppTraitsBase::ShowAssertDialog(msg);
|
||||||
#endif // !wxUSE_MSGDLG/wxUSE_MSGDLG
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxGUIAppTraitsBase::HasStderr()
|
bool wxGUIAppTraitsBase::HasStderr()
|
||||||
|
@ -354,55 +354,60 @@ extern "C"
|
|||||||
bool wxGUIAppTraits::ShowAssertDialog(const wxString& msg)
|
bool wxGUIAppTraits::ShowAssertDialog(const wxString& msg)
|
||||||
{
|
{
|
||||||
#if wxDEBUG_LEVEL
|
#if wxDEBUG_LEVEL
|
||||||
// under GTK2 we prefer to use a dialog widget written using directly in
|
// we can't show the dialog from another thread
|
||||||
// GTK+ as use a dialog written using wxWidgets would need the wxWidgets
|
if ( wxIsMainThread() )
|
||||||
// idle processing to work correctly which might not be the case when
|
{
|
||||||
// assert happens
|
// under GTK2 we prefer to use a dialog widget written using directly
|
||||||
GtkWidget *dialog = gtk_assert_dialog_new();
|
// in GTK+ as use a dialog written using wxWidgets would need the
|
||||||
gtk_assert_dialog_set_message(GTK_ASSERT_DIALOG(dialog), msg.mb_str());
|
// wxWidgets idle processing to work correctly which might not be the
|
||||||
|
// case when assert happens
|
||||||
|
GtkWidget *dialog = gtk_assert_dialog_new();
|
||||||
|
gtk_assert_dialog_set_message(GTK_ASSERT_DIALOG(dialog), msg.mb_str());
|
||||||
|
|
||||||
#if wxUSE_STACKWALKER
|
#if wxUSE_STACKWALKER
|
||||||
// don't show more than maxLines or we could get a dialog too tall to be
|
// don't show more than maxLines or we could get a dialog too tall to
|
||||||
// shown on screen: 20 should be ok everywhere as even with 15 pixel high
|
// be shown on screen: 20 should be ok everywhere as even with 15 pixel
|
||||||
// characters it is still only 300 pixels...
|
// high characters it is still only 300 pixels...
|
||||||
static const int maxLines = 20;
|
static const int maxLines = 20;
|
||||||
|
|
||||||
// save current stack frame...
|
// save current stack frame...
|
||||||
StackDump dump(GTK_ASSERT_DIALOG(dialog));
|
StackDump dump(GTK_ASSERT_DIALOG(dialog));
|
||||||
dump.SaveStack(maxLines);
|
dump.SaveStack(maxLines);
|
||||||
|
|
||||||
// ...but process it only if the user needs it
|
// ...but process it only if the user needs it
|
||||||
gtk_assert_dialog_set_backtrace_callback(GTK_ASSERT_DIALOG(dialog),
|
gtk_assert_dialog_set_backtrace_callback
|
||||||
(GtkAssertDialogStackFrameCallback)get_stackframe_callback,
|
(
|
||||||
&dump);
|
GTK_ASSERT_DIALOG(dialog),
|
||||||
#endif // wxUSE_STACKWALKER
|
(GtkAssertDialogStackFrameCallback)get_stackframe_callback,
|
||||||
|
&dump
|
||||||
|
);
|
||||||
|
#endif // wxUSE_STACKWALKER
|
||||||
|
|
||||||
gint result = gtk_dialog_run(GTK_DIALOG (dialog));
|
gint result = gtk_dialog_run(GTK_DIALOG (dialog));
|
||||||
bool returnCode = false;
|
bool returnCode = false;
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case GTK_ASSERT_DIALOG_STOP:
|
case GTK_ASSERT_DIALOG_STOP:
|
||||||
wxTrap();
|
wxTrap();
|
||||||
break;
|
break;
|
||||||
case GTK_ASSERT_DIALOG_CONTINUE:
|
case GTK_ASSERT_DIALOG_CONTINUE:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
|
case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
|
||||||
// no more asserts
|
// no more asserts
|
||||||
returnCode = true;
|
returnCode = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
wxFAIL_MSG( wxT("unexpected return code from GtkAssertDialog") );
|
wxFAIL_MSG( wxT("unexpected return code from GtkAssertDialog") );
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
return returnCode;
|
||||||
}
|
}
|
||||||
|
#endif // wxDEBUG_LEVEL
|
||||||
|
|
||||||
gtk_widget_destroy(dialog);
|
return wxAppTraitsBase::ShowAssertDialog(msg);
|
||||||
return returnCode;
|
|
||||||
#else // !wxDEBUG_LEVEL
|
|
||||||
// this function is never called in this case
|
|
||||||
wxUnusedVar(msg);
|
|
||||||
return false;
|
|
||||||
#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString wxGUIAppTraits::GetDesktopEnvironment() const
|
wxString wxGUIAppTraits::GetDesktopEnvironment() const
|
||||||
|
Loading…
Reference in New Issue
Block a user