From 00414faf347879152e3a2eb9a3603042631f260b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 26 Dec 2008 22:50:50 +0000 Subject: [PATCH] use DispatchTimeout() and/or select() with timeout instead of polling loop in wxSocket::DoWait() git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57572 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/private/socket.h | 9 +- src/common/socket.cpp | 174 ++++++++++++++++-------------------- 2 files changed, 84 insertions(+), 99 deletions(-) diff --git a/include/wx/private/socket.h b/include/wx/private/socket.h index d953f082ec..078593477f 100644 --- a/include/wx/private/socket.h +++ b/include/wx/private/socket.h @@ -255,7 +255,14 @@ public: virtual int Read(void *buffer, int size) = 0; virtual int Write(const void *buffer, int size) = 0; - wxSocketEventFlags Select(wxSocketEventFlags flags); + // basically a wrapper for select(): returns the condition of the socket, + // blocking for not longer than timeout ms for something to become + // available + // + // flags defines what kind of conditions we're interested in, the return + // value is composed of a (possibly empty) subset of the bits set in flags + wxSocketEventFlags Select(wxSocketEventFlags flags, + unsigned long timeout = 0); virtual wxSocketImpl *WaitConnection(wxSocketBase& wxsocket) = 0; diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 221af68ee4..071b0d5396 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -69,6 +69,21 @@ IMPLEMENT_CLASS(wxSocketClient, wxSocketBase) IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase) IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent) +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +namespace +{ + +void SetTimeValFromMS(timeval& tv, unsigned long ms) +{ + tv.tv_sec = (ms / 1000); + tv.tv_usec = (ms % 1000) * 1000; +} + +} // anonymous namespace + // -------------------------------------------------------------------------- // private classes // -------------------------------------------------------------------------- @@ -390,8 +405,7 @@ void wxSocketImpl::Shutdown() */ void wxSocketImpl::SetTimeout(unsigned long millis) { - m_timeout.tv_sec = (millis / 1000); - m_timeout.tv_usec = (millis % 1000) * 1000; + SetTimeValFromMS(m_timeout, millis); } void wxSocketImpl::NotifyOnStateChange(wxSocketNotify event) @@ -1106,29 +1120,23 @@ wxSocketBase& wxSocketBase::Discard() // -------------------------------------------------------------------------- /* - * Polls the socket to determine its status. This function will - * check for the events specified in the 'flags' parameter, and - * it will return a mask indicating which operations can be - * performed. This function won't block, regardless of the - * mode (blocking | nonblocking) of the socket. + This function will check for the events specified in the flags parameter, + and it will return a mask indicating which operations can be performed. */ -wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags) +wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags, + unsigned long timeout) { - assert(this); - wxSocketEventFlags result = 0; - fd_set readfds; - fd_set writefds; - fd_set exceptfds; - struct timeval tv; if (m_fd == INVALID_SOCKET) return (wxSOCKET_LOST_FLAG & flags); - /* Do not use a static struct, Linux can garble it */ - tv.tv_sec = 0; - tv.tv_usec = 0; + struct timeval tv; + SetTimeValFromMS(tv, timeout); + fd_set readfds; + fd_set writefds; + fd_set exceptfds; wxFD_ZERO(&readfds); wxFD_ZERO(&writefds); wxFD_ZERO(&exceptfds); @@ -1211,12 +1219,6 @@ wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags) return (result | m_detected) & flags; } -// All Wait functions poll the socket using Select() to -// check for the specified combination of conditions, until one -// of these conditions become true, an error occurs, or the -// timeout elapses. The polling loop runs the event loop so that -// this won't block the GUI. - bool wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags) { @@ -1247,33 +1249,55 @@ wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags) eventLoop = NULL; } - // reset them before starting to wait - m_eventsgot = 0; - - // Wait in an active polling loop: notice that the loop is executed at - // least once, even if timeout is 0 (i.e. polling). + // Wait until we receive the event we're waiting for or the timeout expires + // (but note that we always execute the loop at least once, even if timeout + // is 0 as this is used for polling) bool gotEvent = false; - for ( ;; ) + for ( bool firstTime = true; !m_interrupt ; firstTime = false ) { + long timeLeft = wxMilliClockToLong(timeEnd - wxGetLocalTimeMillis()); + if ( timeLeft < 0 ) + { + if ( !firstTime ) + break; // timed out + + timeLeft = 0; + } + + // This function is only called if wxSOCKET_BLOCK flag was not used and + // so we should dispatch the events if there is an event loop capable + // of doing it. wxSocketEventFlags events; if ( eventLoop ) { - // This function is only called if wxSOCKET_BLOCK flag was not used - // and so we should dispatch the events if there is an event loop - // capable of doing it. - if ( eventLoop->Pending() ) - eventLoop->Dispatch(); + // reset them before starting to wait + m_eventsgot = 0; + + eventLoop->DispatchTimeout(timeLeft); events = m_eventsgot; } - else + else // no event loop or waiting in another thread { - // We always stop waiting when the connection is lost as it doesn't - // make sense to continue further, even if wxSOCKET_LOST_FLAG is - // not specified in flags to wait for. - events = m_impl->Select(flags | wxSOCKET_LOST_FLAG); + // as explained below, we should always check for wxSOCKET_LOST_FLAG + events = m_impl->Select(flags | wxSOCKET_LOST_FLAG, timeLeft); } + // always check for wxSOCKET_LOST_FLAG, even if flags doesn't include + // it, as continuing to wait for anything else after getting it is + // pointless + if ( events & wxSOCKET_LOST_FLAG ) + { + m_connected = false; + m_establishing = false; + if ( flags & wxSOCKET_LOST_FLAG ) + gotEvent = true; + break; + } + + // otherwise mask out the bits we're not interested in + events &= flags; + // Incoming connection (server) or connection established (client)? if ( events & wxSOCKET_CONNECTION_FLAG ) { @@ -1289,34 +1313,6 @@ wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags) gotEvent = true; break; } - - // Connection lost - if ( events & wxSOCKET_LOST_FLAG ) - { - m_connected = false; - m_establishing = false; - if ( flags & wxSOCKET_LOST_FLAG ) - gotEvent = true; - break; - } - - if ( m_interrupt ) - break; - - // Wait more? - const wxMilliClock_t timeNow = wxGetLocalTimeMillis(); - if ( timeNow >= timeEnd ) - break; - -#if wxUSE_THREADS - // no event loop or waiting in another thread - if ( !eventLoop ) - { - // We're busy waiting but at least give up the rest of our current - // time slice. - wxThread::Yield(); - } -#endif // wxUSE_THREADS } return gotEvent; @@ -1467,37 +1463,6 @@ void wxSocketBase::SetFlags(wxSocketFlags flags) void wxSocketBase::OnRequest(wxSocketNotify notification) { - switch ( notification ) - { - case wxSOCKET_CONNECTION: - m_establishing = false; - m_connected = true; - break; - - // If we are in the middle of a R/W operation, do not - // propagate events to users. Also, filter 'late' events - // which are no longer valid. - - case wxSOCKET_INPUT: - if (m_reading || !m_impl->Select(wxSOCKET_INPUT_FLAG)) - return; - break; - - case wxSOCKET_OUTPUT: - if (m_writing || !m_impl->Select(wxSOCKET_OUTPUT_FLAG)) - return; - break; - - case wxSOCKET_LOST: - m_connected = false; - m_establishing = false; - break; - - case wxSOCKET_MAX_EVENT: - wxFAIL_MSG( "unexpected notification" ); - return; - } - wxSocketEventFlags flag = 0; switch ( notification ) { @@ -1528,6 +1493,19 @@ void wxSocketBase::OnRequest(wxSocketNotify notification) // send the wx event if enabled and we're interested in it if ( m_notify && (m_eventmask & flag) && m_handler ) { + // If we are in the middle of a R/W operation, do not propagate events + // to users. Also, filter 'late' events which are no longer valid. + if ( notification == wxSOCKET_INPUT ) + { + if ( m_reading || !m_impl->Select(wxSOCKET_INPUT_FLAG) ) + return; + } + else if ( notification == wxSOCKET_OUTPUT ) + { + if ( m_writing || !m_impl->Select(wxSOCKET_OUTPUT_FLAG) ) + return; + } + wxSocketEvent event(m_id); event.m_event = notification; event.m_clientData = m_clientData;