From: Vadim Zeitlin Date: Wed, 3 Jul 2013 00:29:05 +0000 (+0000) Subject: Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/a25b76f5f92c3cc116cb4ca53e8e5a6bcd3ff42f Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource. Use OS X socket APIs for monitoring file descriptors. They are more flexible than CFFileDescriptor functions and can be used with any descriptors, not necessarily the socket ones. See #10258. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74342 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/osx/evtloopsrc.h b/include/wx/osx/evtloopsrc.h index 646190eb72..aff48d6bf9 100644 --- a/include/wx/osx/evtloopsrc.h +++ b/include/wx/osx/evtloopsrc.h @@ -11,7 +11,7 @@ #ifndef _WX_OSX_EVTLOOPSRC_H_ #define _WX_OSX_EVTLOOPSRC_H_ -typedef struct __CFFileDescriptor *CFFileDescriptorRef; +typedef struct __CFSocket* CFSocketRef; // ---------------------------------------------------------------------------- // wxCFEventLoopSource: CoreFoundation-based wxEventLoopSource for OS X @@ -20,19 +20,23 @@ typedef struct __CFFileDescriptor *CFFileDescriptorRef; class WXDLLIMPEXP_BASE wxCFEventLoopSource : public wxEventLoopSource { public: + // Create a new source in uninitialized state, call InitSocketRef() later + // to associate it with the socket it is going to use. wxCFEventLoopSource(wxEventLoopSourceHandler *handler, int flags) : wxEventLoopSource(handler, flags) { - m_cffd = NULL; + m_cfSocket = NULL; } - // we take ownership of this CFFileDescriptorRef - void SetFileDescriptor(CFFileDescriptorRef cffd); + // Finish initialization of the event loop source by providing the + // associated socket. This object takes ownership of it and will release it. + void InitSourceSocket(CFSocketRef cfSocket); + // Destructor deletes the associated socket. virtual ~wxCFEventLoopSource(); private: - CFFileDescriptorRef m_cffd; + CFSocketRef m_cfSocket; wxDECLARE_NO_COPY_CLASS(wxCFEventLoopSource); }; diff --git a/src/osx/core/evtloop_cf.cpp b/src/osx/core/evtloop_cf.cpp index 704a833413..3ae62e9ba1 100644 --- a/src/osx/core/evtloop_cf.cpp +++ b/src/osx/core/evtloop_cf.cpp @@ -42,23 +42,28 @@ #include "wx/nonownedwnd.h" #endif +#include + // ============================================================================ // wxCFEventLoopSource and wxCFEventLoop implementation // ============================================================================ #if wxUSE_EVENTLOOP_SOURCE -void wxCFEventLoopSource::SetFileDescriptor(CFFileDescriptorRef cffd) +void wxCFEventLoopSource::InitSourceSocket(CFSocketRef cfSocket) { - wxASSERT_MSG( !m_cffd, "shouldn't be called more than once" ); + wxASSERT_MSG( !m_cfSocket, "shouldn't be called more than once" ); - m_cffd = cffd; + m_cfSocket = cfSocket; } wxCFEventLoopSource::~wxCFEventLoopSource() { - if ( m_cffd ) - CFRelease(m_cffd); + if ( m_cfSocket ) + { + CFSocketInvalidate(m_cfSocket); + CFRelease(m_cfSocket); + } } #endif // wxUSE_EVENTLOOP_SOURCE diff --git a/src/osx/core/utilsexc_cf.cpp b/src/osx/core/utilsexc_cf.cpp index 221bbb322d..e5881f4b6d 100644 --- a/src/osx/core/utilsexc_cf.cpp +++ b/src/osx/core/utilsexc_cf.cpp @@ -28,7 +28,6 @@ #include -#include #include /*! @@ -114,34 +113,36 @@ int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd) namespace { -void EnableDescriptorCallBacks(CFFileDescriptorRef cffd, int flags) -{ - if ( flags & wxEVENT_SOURCE_INPUT ) - CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack); - if ( flags & wxEVENT_SOURCE_OUTPUT ) - CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack); -} - +extern "C" void -wx_cffiledescriptor_callback(CFFileDescriptorRef cffd, - CFOptionFlags flags, - void *ctxData) +wx_socket_callback(CFSocketRef WXUNUSED(s), + CFSocketCallBackType callbackType, + CFDataRef WXUNUSED(address), + void const *WXUNUSED(data), + void *ctxData) { wxLogTrace(wxTRACE_EVT_SOURCE, - "CFFileDescriptor callback, flags=%d", flags); + "CFSocket callback, type=%d", callbackType); wxCFEventLoopSource * const source = static_cast(ctxData); wxEventLoopSourceHandler * const handler = source->GetHandler(); - if ( flags & kCFFileDescriptorReadCallBack ) - handler->OnReadWaiting(); - if ( flags & kCFFileDescriptorWriteCallBack ) - handler->OnWriteWaiting(); - // we need to re-enable callbacks to be called again - EnableDescriptorCallBacks(cffd, source->GetFlags()); + switch ( callbackType ) + { + case kCFSocketReadCallBack: + handler->OnReadWaiting(); + break; + + case kCFSocketWriteCallBack: + handler->OnWriteWaiting(); + break; + + default: + wxFAIL_MSG( "Unexpected callback type." ); + } } } // anonymous namespace @@ -157,31 +158,63 @@ public: wxScopedPtr source(new wxCFEventLoopSource(handler, flags)); - CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL }; - wxCFRef - cffd(CFFileDescriptorCreate - ( - kCFAllocatorDefault, - fd, - true, // close on invalidate - wx_cffiledescriptor_callback, - &ctx - )); - if ( !cffd ) + CFSocketContext context = { 0, source.get(), NULL, NULL, NULL }; + + int callbackTypes = 0; + if ( flags & wxEVENT_SOURCE_INPUT ) + callbackTypes |= kCFSocketReadCallBack; + if ( flags & wxEVENT_SOURCE_OUTPUT ) + callbackTypes |= kCFSocketWriteCallBack; + + wxCFRef + cfSocket(CFSocketCreateWithNative + ( + kCFAllocatorDefault, + fd, + callbackTypes, + &wx_socket_callback, + &context + )); + + if ( !cfSocket ) + { + wxLogError(wxS("Failed to create event loop source socket.")); return NULL; + } + + // Adjust the socket options to suit our needs: + CFOptionFlags sockopt = CFSocketGetSocketFlags(cfSocket); + + // First, by default, write callback is not called repeatedly when data + // can be written to the socket but we need this behaviour so request + // it explicitly. + if ( flags & wxEVENT_SOURCE_OUTPUT ) + sockopt |= kCFSocketAutomaticallyReenableWriteCallBack; + + // Second, we use the socket to monitor the FD but it doesn't own it, + // so prevent the FD from being closed when the socket is invalidated. + sockopt &= ~kCFSocketCloseOnInvalidate; + + CFSocketSetSocketFlags(cfSocket, sockopt); wxCFRef - cfsrc(CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0)); - if ( !cfsrc ) + runLoopSource(CFSocketCreateRunLoopSource + ( + kCFAllocatorDefault, + cfSocket, + 0 // Lowest index means highest priority + )); + if ( !runLoopSource ) + { + wxLogError(wxS("Failed to create low level event loop source.")); + CFSocketInvalidate(cfSocket); return NULL; + } - CFRunLoopRef cfloop = CFRunLoopGetCurrent(); - CFRunLoopAddSource(cfloop, cfsrc, kCFRunLoopDefaultMode); - - // Enable the callbacks initially. - EnableDescriptorCallBacks(cffd, source->GetFlags()); + // Save the socket so that we can remove it later if asked to. + source->InitSourceSocket(cfSocket.release()); - source->SetFileDescriptor(cffd.release()); + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); return source.release(); }