]> git.saurik.com Git - wxWidgets.git/commitdiff
Better handling for asserts in non-main threads.
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 9 Dec 2009 14:59:08 +0000 (14:59 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 9 Dec 2009 14:59:08 +0000 (14:59 +0000)
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

samples/except/except.cpp
src/common/appbase.cpp
src/common/appcmn.cpp
src/gtk/utilsgtk.cpp

index f22536923415ba66709fd31fba0c67157ea277e5..7ea7b7b159844b8a5e059159f417fdf3822f72be 100644 (file)
@@ -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;
index f2bed9aca81dc2336602f5fe32fed92659826434..fbc1cf78bad0dc498344d23e03a29548b52a5718 100644 (file)
@@ -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
index ea3a17c7c9cf12fcc26a210d8df3c27fbaea6233..ca3c6fdc7504c616cd67a079c5522643e774e2e4 100644 (file)
@@ -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);
-
-    const wxString stackTrace = GetAssertStackTrace();
-    if ( !stackTrace.empty() )
-        msgDlg << wxT("\n\nCall stack:\n") << stackTrace;
+        // 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;
 #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()
index bfcbbc8ab20497ec8a771cf321381e7600418f1f..a8d0888c7c2afaed2d8c6530c6bc56ff445798c5 100644 (file)
@@ -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;
-
-    // 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
-
-    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") );
+        // 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);
+
+        // ...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;
+
+            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