]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/utilsunx.cpp
fix for glib critical errors when closing an mdi child
[wxWidgets.git] / src / unix / utilsunx.cpp
index 8b42a135c073d1f43c62d6b0b492ec10f0353830..12d1e9eb06794d58c146313355cb5a191b26dc1b 100644 (file)
 #include "wx/unix/execute.h"
 #include "wx/unix/private.h"
 
+#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
+#include "wx/private/fdiodispatcher.h"
+#endif
+
 #include <pwd.h>
 #include <sys/wait.h>       // waitpid()
 
@@ -1220,14 +1224,23 @@ bool wxHandleFatalExceptions(bool doit)
 
 #if wxUSE_GUI
 
-#if __DARWIN__
+#ifdef __DARWIN__
     #include <sys/errno.h>
 #endif
 // ----------------------------------------------------------------------------
 // wxExecute support
 // ----------------------------------------------------------------------------
 
-#define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)))
+/*
+    NOTE: If this proves not to work well for wxMac then move back to the old
+    behavior.  If, however, it proves to work just fine, nuke all of the code
+    for the old behavior.  I strongly suggest backporting this to 2.8 as well.
+    However, beware that while you can nuke the old code here, you cannot
+    nuke the wxAddProcessCallbackForPid from the 2.8 branch (found in
+    utilsexc_cf since it's an exported symbol).
+ */
+// #define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__))
+#define USE_OLD_DARWIN_END_PROCESS_DETECT 0
 
 // wxMac/wxCocoa don't use the same process end detection mechanisms so we don't
 // need wxExecute-related helpers for them
@@ -1383,8 +1396,8 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
         }
         else // !wxEXEC_NOEVENTS
         {
-            // endProcData->pid will be set to 0 from GTK_EndProcessDetector when the
-            // process terminates
+            // endProcData->pid will be set to 0 from
+            // wxHandleProcessTermination when the process terminates
             while ( endProcData->pid != 0 )
             {
                 bool idle = true;
@@ -1427,6 +1440,78 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
     }
 }
 
+#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
+struct wxEndProcessFDIOHandler : public wxFDIOHandler
+{
+    wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
+        : m_data(data), m_fd(fd)
+    {}
+
+    virtual void OnReadWaiting()
+        { wxFAIL_MSG("this isn't supposed to happen"); }
+    virtual void OnWriteWaiting()
+        { wxFAIL_MSG("this isn't supposed to happen"); }
+
+    virtual void OnExceptionWaiting()
+    {
+        int pid = (m_data->pid > 0) ? m_data->pid : -(m_data->pid);
+        int status = 0;
+
+        // has the process really terminated?
+        int rc = waitpid(pid, &status, WNOHANG);
+        if ( rc == 0 )
+        {
+            // This can only happen if the child application closes our dummy
+            // pipe that is used to monitor its lifetime; in that case, our
+            // best bet is to pretend the process did terminate, because
+            // otherwise wxExecute() would hang indefinitely
+            // (OnExceptionWaiting() won't be called again, the descriptor
+            // is closed now).
+            wxLogDebug("Child process (PID %i) still alive, even though notification was received that it terminated.", pid);
+        }
+        else if ( rc == -1 )
+        {
+            // As above, if waitpid() fails, the best we can do is to log the
+            // error and pretend the child terminated:
+            wxLogSysError(_("Failed to check child process' status"));
+        }
+
+        // set exit code to -1 if something bad happened
+        m_data->exitcode = (rc > 0 && WIFEXITED(status))
+                           ? WEXITSTATUS(status)
+                           : -1;
+
+        wxLogTrace("exec",
+                   "Child process (PID %i) terminated with exit code %i",
+                   pid, m_data->exitcode);
+
+        // child exited, end waiting
+        wxFDIODispatcher::Get()->UnregisterFD(m_fd);
+        close(m_fd);
+
+        m_data->fdioHandler = NULL;
+        wxHandleProcessTermination(m_data);
+
+        delete this;
+    }
+
+    wxEndProcessData *m_data;
+    int m_fd;
+};
+
+int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+{
+    proc_data->fdioHandler = new wxEndProcessFDIOHandler(proc_data, fd);
+    wxFDIODispatcher::Get()->RegisterFD
+                             (
+                                 fd,
+                                 proc_data->fdioHandler,
+                                 wxFDIO_EXCEPTION
+                             );
+    return fd; // unused, but return something unique for the tag
+}
+#endif // wxHAS_GENERIC_PROCESS_CALLBACK
+
 #endif // wxUSE_GUI
 #if wxUSE_BASE