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
This commit is contained in:
Vadim Zeitlin
2008-12-26 22:50:50 +00:00
parent 9af42efda6
commit 00414faf34
2 changed files with 84 additions and 99 deletions

View File

@@ -255,7 +255,14 @@ public:
virtual int Read(void *buffer, int size) = 0; virtual int Read(void *buffer, int size) = 0;
virtual int Write(const 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; virtual wxSocketImpl *WaitConnection(wxSocketBase& wxsocket) = 0;

View File

@@ -69,6 +69,21 @@ IMPLEMENT_CLASS(wxSocketClient, wxSocketBase)
IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase) IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase)
IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent) 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 // private classes
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -390,8 +405,7 @@ void wxSocketImpl::Shutdown()
*/ */
void wxSocketImpl::SetTimeout(unsigned long millis) void wxSocketImpl::SetTimeout(unsigned long millis)
{ {
m_timeout.tv_sec = (millis / 1000); SetTimeValFromMS(m_timeout, millis);
m_timeout.tv_usec = (millis % 1000) * 1000;
} }
void wxSocketImpl::NotifyOnStateChange(wxSocketNotify event) void wxSocketImpl::NotifyOnStateChange(wxSocketNotify event)
@@ -1106,29 +1120,23 @@ wxSocketBase& wxSocketBase::Discard()
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/* /*
* Polls the socket to determine its status. This function will This function will check for the events specified in the flags parameter,
* check for the events specified in the 'flags' parameter, and and it will return a mask indicating which operations can be performed.
* 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.
*/ */
wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags) wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags,
unsigned long timeout)
{ {
assert(this);
wxSocketEventFlags result = 0; wxSocketEventFlags result = 0;
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
struct timeval tv;
if (m_fd == INVALID_SOCKET) if (m_fd == INVALID_SOCKET)
return (wxSOCKET_LOST_FLAG & flags); return (wxSOCKET_LOST_FLAG & flags);
/* Do not use a static struct, Linux can garble it */ struct timeval tv;
tv.tv_sec = 0; SetTimeValFromMS(tv, timeout);
tv.tv_usec = 0;
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
wxFD_ZERO(&readfds); wxFD_ZERO(&readfds);
wxFD_ZERO(&writefds); wxFD_ZERO(&writefds);
wxFD_ZERO(&exceptfds); wxFD_ZERO(&exceptfds);
@@ -1211,12 +1219,6 @@ wxSocketEventFlags wxSocketImpl::Select(wxSocketEventFlags flags)
return (result | m_detected) & 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 bool
wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags) wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags)
{ {
@@ -1247,33 +1249,55 @@ wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags)
eventLoop = NULL; eventLoop = NULL;
} }
// reset them before starting to wait // Wait until we receive the event we're waiting for or the timeout expires
m_eventsgot = 0; // (but note that we always execute the loop at least once, even if timeout
// is 0 as this is used for polling)
// Wait in an active polling loop: notice that the loop is executed at
// least once, even if timeout is 0 (i.e. polling).
bool gotEvent = false; 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; wxSocketEventFlags events;
if ( eventLoop ) if ( eventLoop )
{ {
// This function is only called if wxSOCKET_BLOCK flag was not used // reset them before starting to wait
// and so we should dispatch the events if there is an event loop m_eventsgot = 0;
// capable of doing it.
if ( eventLoop->Pending() ) eventLoop->DispatchTimeout(timeLeft);
eventLoop->Dispatch();
events = m_eventsgot; 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 // as explained below, we should always check for wxSOCKET_LOST_FLAG
// make sense to continue further, even if wxSOCKET_LOST_FLAG is events = m_impl->Select(flags | wxSOCKET_LOST_FLAG, timeLeft);
// not specified in flags to wait for.
events = m_impl->Select(flags | wxSOCKET_LOST_FLAG);
} }
// 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)? // Incoming connection (server) or connection established (client)?
if ( events & wxSOCKET_CONNECTION_FLAG ) if ( events & wxSOCKET_CONNECTION_FLAG )
{ {
@@ -1289,34 +1313,6 @@ wxSocketBase::DoWait(long seconds, long milliseconds, wxSocketEventFlags flags)
gotEvent = true; gotEvent = true;
break; 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; return gotEvent;
@@ -1467,37 +1463,6 @@ void wxSocketBase::SetFlags(wxSocketFlags flags)
void wxSocketBase::OnRequest(wxSocketNotify notification) 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; wxSocketEventFlags flag = 0;
switch ( notification ) switch ( notification )
{ {
@@ -1528,6 +1493,19 @@ void wxSocketBase::OnRequest(wxSocketNotify notification)
// send the wx event if enabled and we're interested in it // send the wx event if enabled and we're interested in it
if ( m_notify && (m_eventmask & flag) && m_handler ) 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); wxSocketEvent event(m_id);
event.m_event = notification; event.m_event = notification;
event.m_clientData = m_clientData; event.m_clientData = m_clientData;