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/msgdlg.h"
|
||||
#include "wx/icon.h"
|
||||
|
||||
#include "wx/thread.h"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -128,8 +130,13 @@ protected:
|
||||
// catch exceptions which occur in MyFrame methods here
|
||||
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);
|
||||
#if wxUSE_THREADS
|
||||
void OnShowAssertInThread(wxCommandEvent& event);
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
private:
|
||||
// any class wishing to process wxWidgets events must use this macro
|
||||
@ -185,6 +192,9 @@ enum
|
||||
Except_HandleCrash,
|
||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||
Except_ShowAssert,
|
||||
#if wxUSE_THREADS
|
||||
Except_ShowAssertInThread,
|
||||
#endif // wxUSE_THREADS
|
||||
Except_Dialog,
|
||||
|
||||
Except_Quit = wxID_EXIT,
|
||||
@ -211,6 +221,9 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
|
||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||
EVT_MENU(Except_ShowAssert, MyFrame::OnShowAssert)
|
||||
#if wxUSE_THREADS
|
||||
EVT_MENU(Except_ShowAssertInThread, MyFrame::OnShowAssertInThread)
|
||||
#endif // wxUSE_THREADS
|
||||
END_EVENT_TABLE()
|
||||
|
||||
BEGIN_EVENT_TABLE(MyDialog, wxDialog)
|
||||
@ -301,14 +314,17 @@ void MyApp::OnAssertFailure(const wxChar *file,
|
||||
const wxChar *cond,
|
||||
const wxChar *msg)
|
||||
{
|
||||
if ( wxMessageBox
|
||||
(
|
||||
wxString::Format("An assert failed in %s().", func) +
|
||||
"\n"
|
||||
"Do you want to call the default assert handler?",
|
||||
"wxExcept Sample",
|
||||
wxYES_NO | wxICON_QUESTION
|
||||
) == wxYES )
|
||||
// take care to not show the message box from a worker thread, this doesn't
|
||||
// work as it doesn't have any event loop
|
||||
if ( !wxIsMainThread() ||
|
||||
wxMessageBox
|
||||
(
|
||||
wxString::Format("An assert failed in %s().", func) +
|
||||
"\n"
|
||||
"Do you want to call the default assert handler?",
|
||||
"wxExcept Sample",
|
||||
wxYES_NO | wxICON_QUESTION
|
||||
) == wxYES )
|
||||
{
|
||||
wxApp::OnAssertFailure(file, line, func, cond, msg);
|
||||
}
|
||||
@ -343,6 +359,10 @@ MyFrame::MyFrame()
|
||||
menuFile->AppendSeparator();
|
||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||
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->Append(Except_Quit, wxT("E&xit\tCtrl-Q"), wxT("Quit this program"));
|
||||
|
||||
@ -443,6 +463,35 @@ void MyFrame::OnShowAssert(wxCommandEvent& WXUNUSED(event))
|
||||
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))
|
||||
{
|
||||
wxString msg;
|
||||
|
@ -1186,11 +1186,9 @@ bool DoShowAssertDialog(const wxString& msg)
|
||||
//case IDNO: nothing to do
|
||||
}
|
||||
#else // !__WXMSW__
|
||||
wxFprintf(stderr, wxT("%s\n"), msg.c_str());
|
||||
fflush(stderr);
|
||||
wxMessageOutputDebug().Output(msg);
|
||||
|
||||
// TODO: ask the user to enter "Y" or "N" on the console?
|
||||
wxTrap();
|
||||
// TODO: ask the user whether to trap on the console?
|
||||
#endif // __WXMSW__/!__WXMSW__
|
||||
|
||||
// continue with the asserts
|
||||
@ -1236,27 +1234,15 @@ void ShowAssertDialog(const wxString& file,
|
||||
// since dialogs cannot be displayed
|
||||
if ( !wxThread::IsMain() )
|
||||
{
|
||||
msg += wxT(" [in child thread]");
|
||||
|
||||
#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();
|
||||
msg += wxString::Format(" [in thread %lx]", wxThread::GetCurrentId());
|
||||
}
|
||||
else
|
||||
#endif // wxUSE_THREADS
|
||||
|
||||
// log the assert in any case
|
||||
wxMessageOutputDebug().Output(msg);
|
||||
|
||||
if ( !s_bNoAsserts )
|
||||
{
|
||||
// send it to the normal log destination
|
||||
wxLogDebug(wxT("%s"), msg.c_str());
|
||||
|
||||
if ( traits )
|
||||
{
|
||||
// delegate showing assert dialog (if possible) to that class
|
||||
|
@ -447,6 +447,7 @@ wxRendererNative *wxGUIAppTraitsBase::CreateRenderer()
|
||||
|
||||
bool wxGUIAppTraitsBase::ShowAssertDialog(const wxString& msg)
|
||||
{
|
||||
#if wxDEBUG_LEVEL
|
||||
// under MSW we prefer to use the base class version using ::MessageBox()
|
||||
// even if wxMessageBox() is available because it has less chances to
|
||||
// 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
|
||||
// course
|
||||
#if defined(__WXMSW__) || defined(__WXDFB__) || !wxUSE_MSGDLG
|
||||
return wxAppTraitsBase::ShowAssertDialog(msg);
|
||||
#else // wxUSE_MSGDLG
|
||||
#if wxDEBUG_LEVEL
|
||||
wxString msgDlg = msg;
|
||||
#if !defined(__WXMSW__) && !defined(__WXDFB__) && wxUSE_MSGDLG
|
||||
|
||||
// we can't (safely) show the GUI dialog from another thread, only do it
|
||||
// for the asserts in the main thread
|
||||
if ( wxIsMainThread() )
|
||||
{
|
||||
wxString msgDlg = msg;
|
||||
|
||||
#if wxUSE_STACKWALKER
|
||||
// on Unix stack frame generation may take some time, depending on the
|
||||
// size of the executable mainly... warn the user that we are working
|
||||
wxFprintf(stderr, wxT("[Debug] Generating a stack trace... please wait"));
|
||||
fflush(stderr);
|
||||
// on Unix stack frame generation may take some time, depending on the
|
||||
// size of the executable mainly... warn the user that we are working
|
||||
wxFprintf(stderr, wxT("[Debug] Generating a stack trace... please wait"));
|
||||
fflush(stderr);
|
||||
|
||||
const wxString stackTrace = GetAssertStackTrace();
|
||||
if ( !stackTrace.empty() )
|
||||
msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
|
||||
const wxString stackTrace = GetAssertStackTrace();
|
||||
if ( !stackTrace.empty() )
|
||||
msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
|
||||
#endif // wxUSE_STACKWALKER
|
||||
|
||||
// this message is intentionally not translated -- it is for
|
||||
// developpers only
|
||||
msgDlg += wxT("\nDo you want to stop the program?\n")
|
||||
wxT("You can also choose [Cancel] to suppress ")
|
||||
wxT("further warnings.");
|
||||
// this message is intentionally not translated -- it is for
|
||||
// developpers only
|
||||
msgDlg += wxT("\nDo you want to stop the program?\n")
|
||||
wxT("You can also choose [Cancel] to suppress ")
|
||||
wxT("further warnings.");
|
||||
|
||||
switch ( wxMessageBox(msgDlg, wxT("wxWidgets Debug Alert"),
|
||||
wxYES_NO | wxCANCEL | wxICON_STOP ) )
|
||||
{
|
||||
case wxYES:
|
||||
wxTrap();
|
||||
break;
|
||||
switch ( wxMessageBox(msgDlg, wxT("wxWidgets Debug Alert"),
|
||||
wxYES_NO | wxCANCEL | wxICON_STOP ) )
|
||||
{
|
||||
case wxYES:
|
||||
wxTrap();
|
||||
break;
|
||||
|
||||
case wxCANCEL:
|
||||
// no more asserts
|
||||
return true;
|
||||
case wxCANCEL:
|
||||
// no more asserts
|
||||
return true;
|
||||
|
||||
//case wxNO: nothing to do
|
||||
//case wxNO: nothing to do
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#else // !wxDEBUG_LEVEL
|
||||
// this function always exists (for ABI compatibility) but is never called
|
||||
// if debug level is 0 and so can simply do nothing then
|
||||
wxUnusedVar(msg);
|
||||
#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
|
||||
#endif // wxUSE_MSGDLG
|
||||
#endif // wxDEBUG_LEVEL
|
||||
|
||||
return false;
|
||||
#endif // !wxUSE_MSGDLG/wxUSE_MSGDLG
|
||||
return wxAppTraitsBase::ShowAssertDialog(msg);
|
||||
}
|
||||
|
||||
bool wxGUIAppTraitsBase::HasStderr()
|
||||
|
@ -354,55 +354,60 @@ extern "C"
|
||||
bool wxGUIAppTraits::ShowAssertDialog(const wxString& msg)
|
||||
{
|
||||
#if wxDEBUG_LEVEL
|
||||
// under GTK2 we prefer to use a dialog widget written using directly in
|
||||
// GTK+ as use a dialog written using wxWidgets would need the 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());
|
||||
// we can't show the dialog from another thread
|
||||
if ( wxIsMainThread() )
|
||||
{
|
||||
// under GTK2 we prefer to use a dialog widget written using directly
|
||||
// in GTK+ as use a dialog written using wxWidgets would need the
|
||||
// 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
|
||||
// don't show more than maxLines or we could get a dialog too tall to be
|
||||
// shown on screen: 20 should be ok everywhere as even with 15 pixel high
|
||||
// characters it is still only 300 pixels...
|
||||
static const int maxLines = 20;
|
||||
// don't show more than maxLines or we could get a dialog too tall to
|
||||
// be shown on screen: 20 should be ok everywhere as even with 15 pixel
|
||||
// high characters it is still only 300 pixels...
|
||||
static const int maxLines = 20;
|
||||
|
||||
// save current stack frame...
|
||||
StackDump dump(GTK_ASSERT_DIALOG(dialog));
|
||||
dump.SaveStack(maxLines);
|
||||
// save current stack frame...
|
||||
StackDump dump(GTK_ASSERT_DIALOG(dialog));
|
||||
dump.SaveStack(maxLines);
|
||||
|
||||
// ...but process it only if the user needs it
|
||||
gtk_assert_dialog_set_backtrace_callback(GTK_ASSERT_DIALOG(dialog),
|
||||
(GtkAssertDialogStackFrameCallback)get_stackframe_callback,
|
||||
&dump);
|
||||
#endif // wxUSE_STACKWALKER
|
||||
// ...but process it only if the user needs it
|
||||
gtk_assert_dialog_set_backtrace_callback
|
||||
(
|
||||
GTK_ASSERT_DIALOG(dialog),
|
||||
(GtkAssertDialogStackFrameCallback)get_stackframe_callback,
|
||||
&dump
|
||||
);
|
||||
#endif // wxUSE_STACKWALKER
|
||||
|
||||
gint result = gtk_dialog_run(GTK_DIALOG (dialog));
|
||||
bool returnCode = false;
|
||||
switch (result)
|
||||
{
|
||||
case GTK_ASSERT_DIALOG_STOP:
|
||||
wxTrap();
|
||||
break;
|
||||
case GTK_ASSERT_DIALOG_CONTINUE:
|
||||
// nothing to do
|
||||
break;
|
||||
case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
|
||||
// no more asserts
|
||||
returnCode = true;
|
||||
break;
|
||||
gint result = gtk_dialog_run(GTK_DIALOG (dialog));
|
||||
bool returnCode = false;
|
||||
switch (result)
|
||||
{
|
||||
case GTK_ASSERT_DIALOG_STOP:
|
||||
wxTrap();
|
||||
break;
|
||||
case GTK_ASSERT_DIALOG_CONTINUE:
|
||||
// nothing to do
|
||||
break;
|
||||
case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
|
||||
// no more asserts
|
||||
returnCode = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxT("unexpected return code from GtkAssertDialog") );
|
||||
default:
|
||||
wxFAIL_MSG( wxT("unexpected return code from GtkAssertDialog") );
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
return returnCode;
|
||||
}
|
||||
#endif // wxDEBUG_LEVEL
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
return returnCode;
|
||||
#else // !wxDEBUG_LEVEL
|
||||
// this function is never called in this case
|
||||
wxUnusedVar(msg);
|
||||
return false;
|
||||
#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
|
||||
return wxAppTraitsBase::ShowAssertDialog(msg);
|
||||
}
|
||||
|
||||
wxString wxGUIAppTraits::GetDesktopEnvironment() const
|
||||
|
Loading…
Reference in New Issue
Block a user