]> git.saurik.com Git - wxWidgets.git/commitdiff
Use CFSocket instead of CFFileDescriptor in wxCFEventLoopSource.
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 3 Jul 2013 00:29:05 +0000 (00:29 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 3 Jul 2013 00:29:05 +0000 (00:29 +0000)
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

include/wx/osx/evtloopsrc.h
src/osx/core/evtloop_cf.cpp
src/osx/core/utilsexc_cf.cpp

index 646190eb7277586cd6de174d820dd1492b449077..aff48d6bf988ac7e6904e31d36b7c9dc54186eb4 100644 (file)
@@ -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);
 };
index 704a8334134328e52694ad5caef368b2eb417440..3ae62e9ba1eaa25910fe9c6616371d5b4f51aadc 100644 (file)
     #include "wx/nonownedwnd.h"
 #endif
 
+#include <CoreFoundation/CFSocket.h>
+
 // ============================================================================
 // 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
index 221bbb322d5f399f3ac29b5df7a19157362728b6..e5881f4b6d9b77eb430b396486c6edad22aa3779 100644 (file)
@@ -28,7 +28,6 @@
 
 #include <sys/wait.h>
 
-#include <CoreFoundation/CFFileDescriptor.h>
 #include <CoreFoundation/CFSocket.h>
 
 /*!
@@ -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<wxCFEventLoopSource *>(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<wxCFEventLoopSource>
             source(new wxCFEventLoopSource(handler, flags));
 
-        CFFileDescriptorContext ctx = { 0, source.get(), NULL, NULL, NULL };
-        wxCFRef<CFFileDescriptorRef>
-            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<CFSocketRef>
+            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<CFRunLoopSourceRef>
-            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();
     }