From 551896d9daf080aea980ddbe5ca12ce5be9489ee Mon Sep 17 00:00:00 2001 From: David Elliott Date: Fri, 19 Oct 2007 03:10:31 +0000 Subject: [PATCH] Add wxAddProcessCallback for both wxMac and wxCocoa and use it for wxCocoa. It can be turned on for wxMac if desired. See utilsunx.cpp comment. NOTE: I can't add the symbol to version-script.in because other platforms have had this symbol even though wxMac/wxCocoa have not. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@49239 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/mac/corefoundation/utilsexc_cf.cpp | 117 +++++++++++++++++++++++++ src/unix/utilsunx.cpp | 26 ++++++ 2 files changed, 143 insertions(+) diff --git a/src/mac/corefoundation/utilsexc_cf.cpp b/src/mac/corefoundation/utilsexc_cf.cpp index ec7d5a2d24..c6be477ae4 100644 --- a/src/mac/corefoundation/utilsexc_cf.cpp +++ b/src/mac/corefoundation/utilsexc_cf.cpp @@ -231,6 +231,123 @@ int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid) #endif // USE_POLLING/!USE_POLLING +///////////////////////////////////////////////////////////////////////////// +// New implementation avoiding mach ports entirely. + +#include + +/*! + Called due to source signal detected by the CFRunLoop. + This is nearly identical to the wxGTK equivalent. + */ +extern "C" void WXCF_EndProcessDetector(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, void const *data, void *info) +{ + wxEndProcessData * const proc_data = static_cast(info); + +/// This code could reasonably be shared between wxMac/wxCocoa and wxGTK /// + // PID is always positive on UNIX but wx uses the sign bit as a flag. + int pid = (proc_data->pid > 0) ? proc_data->pid : -proc_data->pid; + int status = 0; + int rc = waitpid(pid, &status, WNOHANG); + if(rc == 0) + { + // Keep waiting in case we got a spurious notification + // NOTE: In my limited testing, this doesn't happen. + return; + } + + if(rc == -1) + { // Error.. really shouldn't happen but try to gracefully handle it + wxLogLastError(_T("waitpid")); + proc_data->exitcode = -1; + } + else + { // Process ended for some reason + wxASSERT_MSG(rc == pid, _T("unexpected waitpid() return value")); + + if(WIFEXITED(status)) + proc_data->exitcode = WEXITSTATUS(status); + else if(WIFSIGNALED(status)) + // wxGTK doesn't do this but why not? + proc_data->exitcode = -WTERMSIG(status); + else + { // Should NEVER happen according to waitpid docs + wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling")); + proc_data->exitcode = -1; + } + } +/// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK /// + + /* + Either waitpid failed or the process ended successfully. Either way, + we're done. It's not if waitpid is going to magically succeed when + we get fired again. CFSocketInvalidate closes the fd for us and also + invalidates the run loop source for us which should cause it to + release the CFSocket (thus causing it to be deallocated) and remove + itself from the runloop which should release it and cause it to also + be deallocated. Of course, it's possible the RunLoop hangs onto + one or both of them by retaining/releasing them within its stack + frame. However, that shouldn't be depended on. Assume that s is + deallocated due to the following call. + */ + CFSocketInvalidate(s); + + // Now tell wx that the process has ended. + wxHandleProcessTermination(proc_data); +} + +/*! + Implements the GUI-specific wxAddProcessCallback for both wxMac and + wxCocoa using the CFSocket/CFRunLoop API which is available to both. + Takes advantage of the fact that sockets on UNIX are just regular + file descriptors and thus even a non-socket file descriptor can + apparently be used with CFSocket so long as you only tell CFSocket + to do things with it that would be valid for a non-socket fd. + */ +int wxAddProcessCallback(wxEndProcessData *proc_data, int fd) +{ + static int s_last_tag = 0; + CFSocketContext context = + { 0 + , static_cast(proc_data) + , NULL + , NULL + , NULL + }; + CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context); + if(cfSocket == NULL) + { + wxLogError("Failed to create socket for end process detection"); + return 0; + } + CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0); + if(runLoopSource == NULL) + { + wxLogError("Failed to create CFRunLoopSource from CFSocket for end process detection"); + // closes the fd.. we can't really stop it, nor do we necessarily want to. + CFSocketInvalidate(cfSocket); + CFRelease(cfSocket); + return 0; + } + // Now that the run loop source has the socket retained and we no longer + // need to refer to it within this method, we can release it. + CFRelease(cfSocket); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); + // Now that the run loop has the source retained we can release it. + CFRelease(runLoopSource); + + /* + Feed wx some bullshit.. we don't use it since CFSocket helpfully passes + itself into our callback and that's enough to be able to + CFSocketInvalidate it which is all we need to do to get everything we + just created to be deallocated. + */ + return ++s_last_tag; +} + +///////////////////////////////////////////////////////////////////////////// + // NOTE: This doesn't really belong here but this was a handy file to // put it in because it's already compiled for wxCocoa and wxMac GUI lib. #if wxUSE_GUI diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 0e63ebc8e0..f40aedea1f 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -1177,7 +1177,33 @@ bool wxHandleFatalExceptions(bool doit) // wxExecute support // ---------------------------------------------------------------------------- +/* + NOTE: The original code shipped in 2.8 used __DARWIN__ && __WXMAC__ to wrap + the wxGUIAppTraits differences but __DARWIN__ && (__WXMAC__ || __WXCOCOA__) + to decide whether to call wxAddProcessCallbackForPid instead of + wxAddProcessCallback. This define normalizes things so the two match. + + Since wxCocoa was already creating the pipes in its wxGUIAppTraits I + decided to leave that as is and implement wxAddProcessCallback in the + utilsexec_cf.cpp file. I didn't see a reason to wrap that in a __WXCOCOA__ + check since it's valid for both wxMac and wxCocoa. + + Since the existing code is working for wxMac I've left it as is although + do note that the old task_for_pid method still used on PPC machines is + expected to fail in Leopard PPC and theoretically already fails if you run + your PPC app under Rosetta. + + You thus have two choices if you find end process detect broken: + 1) Change the define below such that the new code is used for wxMac. + This is theoretically ABI compatible since the old code still remains + in utilsexec_cf.cpp it's just no longer used by this code. + 2) Change the USE_POLLING define in utilsexc_cf.cpp to 1 unconditionally + This is theoretically not compatible since it removes the + wxMAC_MachPortEndProcessDetect helper function. Though in practice + this shouldn't be a problem since it wasn't prototyped anywhere. + */ #define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__)) +// #define USE_OLD_DARWIN_END_PROCESS_DETECT 0 // wxMac doesn't use the same process end detection mechanisms so we don't // need wxExecute-related helpers for it.