]> git.saurik.com Git - wxWidgets.git/commitdiff
- Added wxAddProcessCallbackForPid function
authorDavid Elliott <dfe@tgwbd.org>
Wed, 26 Mar 2003 17:46:14 +0000 (17:46 +0000)
committerDavid Elliott <dfe@tgwbd.org>
Wed, 26 Mar 2003 17:46:14 +0000 (17:46 +0000)
- Don't create pipeEndProcDetect on Darwin
- Initialize the wxEndProcessData before installing the callback
- Implement wxAddProcessCallbackForPid for Darwin, the code started life
  as an example from ADC and certainly has its fair share of race
  conditions, but with a few hacks it seems to be working okay.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19811 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/unix/execute.h
src/mac/carbon/utilsexc.cpp
src/mac/utilsexc.cpp
src/unix/utilsunx.cpp

index 77a821e42b60d9cb3b3803c88bfa02f9d53711eb..ef2869a512cbf186dbce57f8148e454c163b1185 100644 (file)
@@ -28,5 +28,7 @@ extern void wxHandleProcessTermination(wxEndProcessData *proc_data);
 // this function is called to associate the port-specific callback with the
 // child process. The return valus is port-specific.
 extern int wxAddProcessCallback(wxEndProcessData *proc_data, int fd);
+// For ports (e.g. DARWIN) which can add callbacks based on the pid
+extern int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid);
 
 #endif // _WX_UNIX_EXECUTE_H
index 229ad654f783b4b81e323dd908140f965f5bf120..1d9082a085e3aacf16a4c61214b49682210f89de 100644 (file)
@@ -2,7 +2,7 @@
 // Name:        utilsexec.cpp
 // Purpose:     Execution-related utilities
 // Author:      Stefan Csomor
-// Modified by:
+// Modified by: David Elliott
 // Created:     1998-01-01
 // RCS-ID:      $Id$
 // Copyright:   (c) Stefan Csomor
 #pragma implementation
 #endif
 
+#include "wx/log.h"
 #include "wx/utils.h"
 #ifdef __DARWIN__
 #include "wx/unix/execute.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <CoreFoundation/CFMachPort.h>
 #endif
 
 #include <stdio.h>
@@ -34,9 +39,89 @@ long wxExecute(const wxString& command, int flags, wxProcess *handler)
 #endif
 
 #ifdef __DARWIN__
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
 {
-   wxFAIL_MSG( _T("wxAddProcessCallback() not yet implemented") );
-   return 0;
+       wxEndProcessData *proc_data = (wxEndProcessData*)data;
+       wxLogDebug("Wow.. this actually worked!");
+       int status = 0;
+       int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
+       if(!rc)
+       {
+               wxLogDebug("Mach port was invalidated, but process hasn't terminated!");
+               return;
+       }
+       if((rc != -1) && WIFEXITED(status))
+               proc_data->exitcode = WEXITSTATUS(status);
+       else
+               proc_data->exitcode = -1;
+       wxHandleProcessTermination(proc_data);
+}
+
+int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
+{
+    if(pid < 1)
+        return -1;
+    kern_return_t    kernResult;
+    mach_port_t    taskOfOurProcess;
+    mach_port_t    machPortForProcess;
+    taskOfOurProcess = mach_task_self();
+    if(taskOfOurProcess == MACH_PORT_NULL)
+    {
+        wxLogDebug("No mach_task_self()");
+        return -1;
+    }
+    wxLogDebug("pid=%d",pid);
+    kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
+    if(kernResult != KERN_SUCCESS)
+    {
+        wxLogDebug("no task_for_pid()");
+        // try seeing if it is already dead or something
+        // FIXME: a better method would be to call the callback function
+        // from idle time until the process terminates. Of course, how
+        // likely is it that it will take more than 0.1 seconds for the
+        // mach terminate event to make its way to the BSD subsystem?
+        usleep(100); // sleep for 0.1 seconds
+        wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
+        return -1;
+    }
+    CFMachPortContext termcb_contextinfo;
+    termcb_contextinfo.version = NULL;
+    termcb_contextinfo.info = (void*)proc_data;
+    termcb_contextinfo.retain = NULL;
+    termcb_contextinfo.release = NULL;
+    termcb_contextinfo.copyDescription = NULL;
+    CFMachPortRef    CFMachPortForProcess;
+    Boolean        ShouldFreePort;
+    CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
+    if(!CFMachPortForProcess)
+    {
+        wxLogDebug("No CFMachPortForProcess");
+        mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+        return -1;
+    }
+    if(ShouldFreePort)
+    {
+        kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+        if(kernResult!=KERN_SUCCESS)
+        {
+            wxLogDebug("Couldn't deallocate mach port");
+            return -1;
+        }
+    }
+    CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
+    CFRunLoopSourceRef    runloopsource;
+    runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
+    if(!runloopsource)
+    {
+        wxLogDebug("Couldn't create runloopsource");
+        return -1;
+    }
+    
+    CFRelease(CFMachPortForProcess);
+
+    CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
+    CFRelease(runloopsource);
+    wxLogDebug("Successfully added notification to the runloop");
+    return 0;
 }
 #endif
index 229ad654f783b4b81e323dd908140f965f5bf120..1d9082a085e3aacf16a4c61214b49682210f89de 100644 (file)
@@ -2,7 +2,7 @@
 // Name:        utilsexec.cpp
 // Purpose:     Execution-related utilities
 // Author:      Stefan Csomor
-// Modified by:
+// Modified by: David Elliott
 // Created:     1998-01-01
 // RCS-ID:      $Id$
 // Copyright:   (c) Stefan Csomor
 #pragma implementation
 #endif
 
+#include "wx/log.h"
 #include "wx/utils.h"
 #ifdef __DARWIN__
 #include "wx/unix/execute.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <CoreFoundation/CFMachPort.h>
 #endif
 
 #include <stdio.h>
@@ -34,9 +39,89 @@ long wxExecute(const wxString& command, int flags, wxProcess *handler)
 #endif
 
 #ifdef __DARWIN__
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
 {
-   wxFAIL_MSG( _T("wxAddProcessCallback() not yet implemented") );
-   return 0;
+       wxEndProcessData *proc_data = (wxEndProcessData*)data;
+       wxLogDebug("Wow.. this actually worked!");
+       int status = 0;
+       int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
+       if(!rc)
+       {
+               wxLogDebug("Mach port was invalidated, but process hasn't terminated!");
+               return;
+       }
+       if((rc != -1) && WIFEXITED(status))
+               proc_data->exitcode = WEXITSTATUS(status);
+       else
+               proc_data->exitcode = -1;
+       wxHandleProcessTermination(proc_data);
+}
+
+int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
+{
+    if(pid < 1)
+        return -1;
+    kern_return_t    kernResult;
+    mach_port_t    taskOfOurProcess;
+    mach_port_t    machPortForProcess;
+    taskOfOurProcess = mach_task_self();
+    if(taskOfOurProcess == MACH_PORT_NULL)
+    {
+        wxLogDebug("No mach_task_self()");
+        return -1;
+    }
+    wxLogDebug("pid=%d",pid);
+    kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
+    if(kernResult != KERN_SUCCESS)
+    {
+        wxLogDebug("no task_for_pid()");
+        // try seeing if it is already dead or something
+        // FIXME: a better method would be to call the callback function
+        // from idle time until the process terminates. Of course, how
+        // likely is it that it will take more than 0.1 seconds for the
+        // mach terminate event to make its way to the BSD subsystem?
+        usleep(100); // sleep for 0.1 seconds
+        wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
+        return -1;
+    }
+    CFMachPortContext termcb_contextinfo;
+    termcb_contextinfo.version = NULL;
+    termcb_contextinfo.info = (void*)proc_data;
+    termcb_contextinfo.retain = NULL;
+    termcb_contextinfo.release = NULL;
+    termcb_contextinfo.copyDescription = NULL;
+    CFMachPortRef    CFMachPortForProcess;
+    Boolean        ShouldFreePort;
+    CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
+    if(!CFMachPortForProcess)
+    {
+        wxLogDebug("No CFMachPortForProcess");
+        mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+        return -1;
+    }
+    if(ShouldFreePort)
+    {
+        kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+        if(kernResult!=KERN_SUCCESS)
+        {
+            wxLogDebug("Couldn't deallocate mach port");
+            return -1;
+        }
+    }
+    CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
+    CFRunLoopSourceRef    runloopsource;
+    runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
+    if(!runloopsource)
+    {
+        wxLogDebug("Couldn't create runloopsource");
+        return -1;
+    }
+    
+    CFRelease(CFMachPortForProcess);
+
+    CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
+    CFRelease(runloopsource);
+    wxLogDebug("Successfully added notification to the runloop");
+    return 0;
 }
 #endif
index fd816cb92010d867587cd19032d66c9dd353c1c3..5c76164d10d4cf305b2a1783570802b96eb12d73 100644 (file)
@@ -544,7 +544,7 @@ long wxExecute(wxChar **argv,
     wxChar **mb_argv = argv;
 #endif // Unicode/ANSI
 
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
     // create pipes
     wxPipe pipeEndProcDetect;
     if ( !pipeEndProcDetect.Create() )
@@ -555,7 +555,7 @@ long wxExecute(wxChar **argv,
 
         return ERROR_RETURN_CODE;
     }
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
 
     // pipes for inter process communication
     wxPipe pipeIn,      // stdin
@@ -606,9 +606,9 @@ long wxExecute(wxChar **argv,
                 if ( fd == pipeIn[wxPipe::Read]
                         || fd == pipeOut[wxPipe::Write]
                         || fd == pipeErr[wxPipe::Write]
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
                         || fd == pipeEndProcDetect[wxPipe::Write]
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
                    )
                 {
                     // don't close this one, we still need it
@@ -630,12 +630,12 @@ long wxExecute(wxChar **argv,
         }
 #endif // !__VMS
 
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
         // reading side can be safely closed but we should keep the write one
         // opened
         pipeEndProcDetect.Detach(wxPipe::Write);
         pipeEndProcDetect.Close();
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
 
         // redirect stdin, stdout and stderr
         if ( pipeIn.IsOk() )
@@ -714,6 +714,31 @@ long wxExecute(wxChar **argv,
 #if wxUSE_GUI && !defined(__WXMICROWIN__)
         wxEndProcessData *data = new wxEndProcessData;
 
+        // wxAddProcessCallback is now (with DARWIN) allowed to call the
+        // callback function directly if the process terminates before
+        // the callback can be added to the run loop. Set up the data.
+        if ( flags & wxEXEC_SYNC )
+        {
+            // we may have process for capturing the program output, but it's
+            // not used in wxEndProcessData in the case of sync execution
+            data->process = NULL;
+
+            // sync execution: indicate it by negating the pid
+            data->pid = -pid;
+        }
+        else
+        {
+            // async execution, nothing special to do - caller will be
+            // notified about the process termination if process != NULL, data
+            // will be deleted in GTK_EndProcessDetector
+            data->process  = process;
+            data->pid      = pid;
+        }
+
+
+#if defined(__DARWIN__)
+        data->tag = wxAddProcessCallbackForPid(data,pid);
+#else
         data->tag = wxAddProcessCallback
                     (
                         data,
@@ -721,16 +746,10 @@ long wxExecute(wxChar **argv,
                     );
 
         pipeEndProcDetect.Close();
+#endif // defined(__DARWIN__)
 
         if ( flags & wxEXEC_SYNC )
         {
-            // we may have process for capturing the program output, but it's
-            // not used in wxEndProcessData in the case of sync execution
-            data->process = NULL;
-
-            // sync execution: indicate it by negating the pid
-            data->pid = -pid;
-
             wxBusyCursor bc;
             wxWindowDisabler wd;
 
@@ -756,12 +775,6 @@ long wxExecute(wxChar **argv,
         }
         else // async execution
         {
-            // async execution, nothing special to do - caller will be
-            // notified about the process termination if process != NULL, data
-            // will be deleted in GTK_EndProcessDetector
-            data->process  = process;
-            data->pid      = pid;
-
             return pid;
         }
 #else // !wxUSE_GUI