]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/sockunix.cpp
adapting ownership semantics to cocoa convention
[wxWidgets.git] / src / unix / sockunix.cpp
index 6383b54ac579742187047b7a89f6664e6550475d..0bcaa363b9255e51ee2c07b93b808a1228142b05 100644 (file)
 // wxSocketImpl implementation
 // ============================================================================
 
-/* static */
-wxSocketImpl *wxSocketImpl::Create(wxSocketBase& wxsocket)
-{
-    return new wxSocketImplUnix(wxsocket);
-}
-
-
 wxSocketError wxSocketImplUnix::GetLastError() const
 {
     switch ( errno )
@@ -104,21 +97,39 @@ wxSocketError wxSocketImplUnix::GetLastError() const
     }
 }
 
-void wxSocketImplUnix::DoEnableEvents(bool flag)
+void wxSocketImplUnix::DoEnableEvents(int flags, bool enable)
 {
     wxSocketManager * const manager = wxSocketManager::Get();
-    if ( flag )
+    if (!manager)
+        return;
+
+    if ( enable )
     {
-        manager->Install_Callback(this, wxSOCKET_INPUT);
-        manager->Install_Callback(this, wxSOCKET_OUTPUT);
+        if ( flags & wxSOCKET_INPUT_FLAG )
+            manager->Install_Callback(this, wxSOCKET_INPUT);
+        if ( flags & wxSOCKET_OUTPUT_FLAG )
+            manager->Install_Callback(this, wxSOCKET_OUTPUT);
     }
     else // off
     {
-        manager->Uninstall_Callback(this, wxSOCKET_INPUT);
-        manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
+        if ( flags & wxSOCKET_INPUT_FLAG )
+            manager->Uninstall_Callback(this, wxSOCKET_INPUT);
+        if ( flags & wxSOCKET_OUTPUT_FLAG )
+            manager->Uninstall_Callback(this, wxSOCKET_OUTPUT);
     }
 }
 
+int wxSocketImplUnix::CheckForInput()
+{
+    char c;
+    int rc;
+    do
+    {
+        rc = recv(m_fd, &c, 1, MSG_PEEK);
+    } while ( rc == -1 && errno == EINTR );
+
+    return rc;
+}
 
 void wxSocketImplUnix::OnStateChange(wxSocketNotify event)
 {
@@ -130,87 +141,104 @@ void wxSocketImplUnix::OnStateChange(wxSocketNotify event)
 
 void wxSocketImplUnix::OnReadWaiting()
 {
-  char c;
-
-  if (m_fd == INVALID_SOCKET)
-  {
-    return;
-  }
-
-  int num =  recv(m_fd, &c, 1, MSG_PEEK);
-
-  if (num > 0)
-  {
-    OnStateChange(wxSOCKET_INPUT);
-  }
-  else
-  {
-    if (m_server && m_stream)
-    {
-      OnStateChange(wxSOCKET_CONNECTION);
-    }
-    else if (num == 0)
+    wxASSERT_MSG( m_fd != INVALID_SOCKET, "invalid socket ready for reading?" );
+
+    // we need to disable the read notifications until we read all the data
+    // already available for the socket, otherwise we're going to keep getting
+    // them continuously which is worse than inefficient: as IO notifications
+    // have higher priority than idle events in e.g. GTK+, our pending events
+    // whose handlers typically call Read() which would consume the data and so
+    // stop the notifications flood would never be dispatched at all if the
+    // notifications were not disabled
+    DisableEvents(wxSOCKET_INPUT_FLAG);
+
+
+    // find out what are we going to notify about exactly
+    wxSocketNotify notify;
+
+    // TCP listening sockets become ready for reading when there is a pending
+    // connection
+    if ( m_server && m_stream )
     {
-      if (m_stream)
-      {
-        /* graceful shutdown */
-        OnStateChange(wxSOCKET_LOST);
-      }
-      else
-      {
-        /* Empty datagram received */
-        OnStateChange(wxSOCKET_INPUT);
-      }
+        notify = wxSOCKET_CONNECTION;
     }
-    else
+    else // check if there is really any input available
     {
-      /* Do not throw a lost event in cases where the socket isn't really lost */
-      if ((errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINTR))
-      {
-        OnStateChange(wxSOCKET_INPUT);
-      }
-      else
-      {
-        OnStateChange(wxSOCKET_LOST);
-      }
+        switch ( CheckForInput() )
+        {
+            case 1:
+                notify = wxSOCKET_INPUT;
+                break;
+
+            case 0:
+                // reading 0 bytes for a TCP socket means that the connection
+                // was closed by peer but for UDP it just means that we got an
+                // empty datagram
+                notify = m_stream ? wxSOCKET_LOST : wxSOCKET_INPUT;
+                break;
+
+            default:
+                wxFAIL_MSG( "unexpected CheckForInput() return value" );
+                // fall through
+
+            case -1:
+                if ( GetLastError() == wxSOCKET_WOULDBLOCK )
+                {
+                    // just a spurious wake up
+                    EnableEvents(wxSOCKET_INPUT_FLAG);
+                    return;
+                }
+
+                notify = wxSOCKET_LOST;
+        }
     }
-  }
+
+    OnStateChange(notify);
 }
 
 void wxSocketImplUnix::OnWriteWaiting()
 {
-  if (m_establishing && !m_server)
-  {
-    int error;
-    SOCKOPTLEN_T len = sizeof(error);
+    wxASSERT_MSG( m_fd != INVALID_SOCKET, "invalid socket ready for writing?" );
 
-    m_establishing = false;
+    // see comment in the beginning of OnReadWaiting() above
+    DisableEvents(wxSOCKET_OUTPUT_FLAG);
 
-    getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
 
-    if (error)
-    {
-      OnStateChange(wxSOCKET_LOST);
-    }
-    else
+    // check whether this is a notification for the completion of a
+    // non-blocking connect()
+    if ( m_establishing && !m_server )
     {
-      OnStateChange(wxSOCKET_CONNECTION);
-      /* We have to fire this event by hand because CONNECTION (for clients)
-       * and OUTPUT are internally the same and we just disabled CONNECTION
-       * events with the above macro.
-       */
-      OnStateChange(wxSOCKET_OUTPUT);
+        m_establishing = false;
+
+        // check whether we connected successfully
+        int error;
+        SOCKOPTLEN_T len = sizeof(error);
+
+        getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+
+        if ( error )
+        {
+            OnStateChange(wxSOCKET_LOST);
+            return;
+        }
+
+        OnStateChange(wxSOCKET_CONNECTION);
     }
-  }
-  else
-  {
+
     OnStateChange(wxSOCKET_OUTPUT);
-  }
 }
 
 void wxSocketImplUnix::OnExceptionWaiting()
 {
-    wxFAIL_MSG( "not supposed to be called" );
+    // when using epoll() this is called when an error occurred on the socket
+    // so close it if it hadn't been done yet -- what else can we do?
+    //
+    // notice that we shouldn't be called at all when using select() as we
+    // don't use wxFDIO_EXCEPTION when registering the socket for monitoring
+    // and this is good because select() would call this for any OOB data which
+    // is not necessarily an error
+    if ( m_fd != INVALID_SOCKET )
+        OnStateChange(wxSOCKET_LOST);
 }
 
 #endif  /* wxUSE_SOCKETS */