X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c9bccf239c1506c7eea0bddea44d9e4b76ad1d93..ccd5d46c7b69632eaa231e8fc7801dd5af2faaa8:/src/unix/sockunix.cpp diff --git a/src/unix/sockunix.cpp b/src/unix/sockunix.cpp index 6383b54ac5..51a158cab6 100644 --- a/src/unix/sockunix.cpp +++ b/src/unix/sockunix.cpp @@ -104,21 +104,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,82 +148,91 @@ 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) + // check whether this is a notification for the completion of a + // non-blocking connect() + if ( m_establishing && !m_server ) { - OnStateChange(wxSOCKET_LOST); - } - else - { - 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()