///////////////////////////////////////////////////////////////////////////////
-// Name: src/common/socketevtdispatch.cpp
+// Name: src/common/selectdispatcher.cpp
// Purpose: implements dispatcher for select() call
-// Author: Lukasz Michalski
+// Author: Lukasz Michalski and Vadim Zeitlin
// Created: December 2006
// RCS-ID: $Id$
// Copyright: (c) 2006 Lukasz Michalski
-// License: wxWindows licence
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#if wxUSE_SELECT_DISPATCHER
+
#include "wx/private/selectdispatcher.h"
-#include "wx/module.h"
-#include "wx/timer.h"
#include "wx/unix/private.h"
-#include "wx/log.h"
#ifndef WX_PRECOMP
#include "wx/hash.h"
+ #include "wx/log.h"
+ #include "wx/intl.h"
#endif
-#include <sys/time.h>
-#include <unistd.h>
-
-#ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h>
-#endif
+#include <errno.h>
#define wxSelectDispatcher_Trace wxT("selectdispatcher")
// ============================================================================
// ----------------------------------------------------------------------------
-// wxSelectDispatcher
+// wxSelectSets
// ----------------------------------------------------------------------------
-wxSelectDispatcher* wxSelectDispatcher::ms_instance = NULL;
+int wxSelectSets::ms_flags[wxSelectSets::Max] =
+{
+ wxFDIO_INPUT,
+ wxFDIO_OUTPUT,
+ wxFDIO_EXCEPTION,
+};
-/* static */
-wxSelectDispatcher& wxSelectDispatcher::Get()
+const char *wxSelectSets::ms_names[wxSelectSets::Max] =
{
- if ( !ms_instance )
- ms_instance = new wxSelectDispatcher;
- return *ms_instance;
-}
+ "input",
+ "output",
+ "exceptional",
+};
-void
-wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler* handler, int flags)
+wxSelectSets::Callback wxSelectSets::ms_handlers[wxSelectSets::Max] =
{
- if ((flags & wxSelectInput) == wxSelectInput)
- {
- wxFD_SET(fd, &m_readset);
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for input events"),fd);
- };
+ &wxFDIOHandler::OnReadWaiting,
+ &wxFDIOHandler::OnWriteWaiting,
+ &wxFDIOHandler::OnExceptionWaiting,
+};
- if ((flags & wxSelectOutput) == wxSelectOutput)
+wxSelectSets::wxSelectSets()
+{
+ for ( int n = 0; n < Max; n++ )
{
- wxFD_SET(fd, &m_writeset);
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for output events"),fd);
+ wxFD_ZERO(&m_fds[n]);
}
+}
- if ((flags & wxSelectException) == wxSelectException)
+bool wxSelectSets::HasFD(int fd) const
+{
+ for ( int n = 0; n < Max; n++ )
{
- wxFD_SET(fd, &m_exeptset);
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Registered fd %d for exception events"),fd);
- };
+ if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
+ return true;
+ }
- m_handlers[fd] = handler;
- if (fd > m_maxFD)
- m_maxFD = fd;
+ return false;
}
-wxFDIOHandler*
-wxSelectDispatcher::UnregisterFD(int fd, int flags)
+bool wxSelectSets::SetFD(int fd, int flags)
{
- // GSocket likes to unregister -1 descriptor
- if (fd == -1)
- return NULL;
+ wxCHECK_MSG( fd >= 0, false, wxT("invalid descriptor") );
- if ((flags & wxSelectInput) == wxSelectInput)
+ for ( int n = 0; n < Max; n++ )
{
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from input events"),fd);
- wxFD_CLR(fd, &m_readset);
+ if ( flags & ms_flags[n] )
+ {
+ wxFD_SET(fd, &m_fds[n]);
+ }
+ else if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
+ {
+ wxFD_CLR(fd, &m_fds[n]);
+ }
}
- if ((flags & wxSelectOutput) == wxSelectOutput)
- {
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from output events"),fd);
- wxFD_CLR(fd, &m_writeset);
- }
+ return true;
+}
- if ((flags & wxSelectException) == wxSelectException)
- {
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Unregistered fd %d from exeption events"),fd);
- wxFD_CLR(fd, &m_exeptset);
- };
+int wxSelectSets::Select(int nfds, struct timeval *tv)
+{
+ return select(nfds, &m_fds[Read], &m_fds[Write], &m_fds[Except], tv);
+}
- wxFDIOHandler* ret = NULL;
- wxFDIOHandlerMap::const_iterator it = m_handlers.find(fd);
- if (it != m_handlers.end())
+bool wxSelectSets::Handle(int fd, wxFDIOHandler& handler) const
+{
+ for ( int n = 0; n < Max; n++ )
{
- ret = it->second;
- if (!wxFD_ISSET(fd,&m_readset) && !wxFD_ISSET(fd,&m_writeset) && !wxFD_ISSET(fd,&m_exeptset))
+ if ( wxFD_ISSET(fd, (fd_set*) &m_fds[n]) )
{
- m_handlers.erase(it);
- if ( m_handlers.empty() )
- m_maxFD = 0;
- };
- };
- return ret;
+ wxLogTrace(wxSelectDispatcher_Trace,
+ wxT("Got %s event on fd %d"), ms_names[n], fd);
+ (handler.*ms_handlers[n])();
+ // callback can modify sets and destroy handler
+ // this forces that one event can be processed at one time
+ return true;
+ }
+ }
+
+ return false;
}
-void wxSelectDispatcher::ProcessSets(fd_set* readset, fd_set* writeset, fd_set* exeptset, int max_fd)
+// ----------------------------------------------------------------------------
+// wxSelectDispatcher
+// ----------------------------------------------------------------------------
+
+bool wxSelectDispatcher::RegisterFD(int fd, wxFDIOHandler *handler, int flags)
{
- // it is safe to remove handler from onXXX methods,
- // if you unregister descriptor first.
- wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
- for ( int i = 0; i < max_fd; i++ )
- {
- wxFDIOHandler* handler = NULL;
- if (wxFD_ISSET(i, readset))
- {
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Got read event on fd %d"),i);
- handler = FindHandler(i);
- if (handler != NULL && wxFD_ISSET(i,&m_readset))
- handler->OnReadWaiting(i);
- else
- {
- wxLogError(wxT("Lost fd in read fdset: %d, removing"),i);
- wxFD_CLR(i,&m_readset);
- };
- };
+ if ( !wxMappedFDIODispatcher::RegisterFD(fd, handler, flags) )
+ return false;
- if (wxFD_ISSET(i, writeset))
- {
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Got write event on fd %d"),i);
- if (handler == NULL)
- handler = FindHandler(i);
- if (handler != NULL && wxFD_ISSET(i,&m_writeset))
- handler->OnWriteWaiting(i);
- else
- {
- wxLogError(wxT("Lost fd in write fdset: %d, removing"),i);
- wxFD_CLR(i,&m_writeset);
- };
- };
+ if ( !m_sets.SetFD(fd, flags) )
+ return false;
- if (wxFD_ISSET(i, exeptset))
- {
- wxLogTrace(wxSelectDispatcher_Trace,wxT("Got exception event on fd %d"),i);
- if (handler == NULL)
- handler = FindHandler(i);
- if (handler != NULL && wxFD_ISSET(i,&m_writeset))
- handler->OnExceptionWaiting(i);
- else
- {
- wxLogError(wxT("Lost fd in exept fdset: %d, removing"),i);
- wxFD_CLR(i,&m_exeptset);
- };
- };
- };
+ if ( fd > m_maxFD )
+ m_maxFD = fd;
+
+ wxLogTrace(wxSelectDispatcher_Trace,
+ wxT("Registered fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT), (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
+ return true;
}
-wxFDIOHandler* wxSelectDispatcher::FindHandler(int fd)
+bool wxSelectDispatcher::ModifyFD(int fd, wxFDIOHandler *handler, int flags)
{
- wxFDIOHandlerMap::const_iterator it = m_handlers.find(fd);
- if (it != m_handlers.end())
- return it->second;
- return NULL;
-};
+ if ( !wxMappedFDIODispatcher::ModifyFD(fd, handler, flags) )
+ return false;
+
+ wxASSERT_MSG( fd <= m_maxFD, wxT("logic error: registered fd > m_maxFD?") );
+
+ wxLogTrace(wxSelectDispatcher_Trace,
+ wxT("Modified fd %d: input:%d, output:%d, exceptional:%d"), fd, (flags & wxFDIO_INPUT) == wxFDIO_INPUT, (flags & wxFDIO_OUTPUT) == wxFDIO_OUTPUT, (flags & wxFDIO_EXCEPTION) == wxFDIO_EXCEPTION);
+ return m_sets.SetFD(fd, flags);
+}
-void wxSelectDispatcher::RunLoop(int timeout)
+bool wxSelectDispatcher::UnregisterFD(int fd)
{
- struct timeval tv, *ptv = NULL;
- if ( timeout != wxSELECT_TIMEOUT_INFINITE )
- {
- ptv = &tv;
- tv.tv_sec = 0;
- tv.tv_usec = timeout*10;
- };
+ m_sets.ClearFD(fd);
- int ret;
- do
+ if ( !wxMappedFDIODispatcher::UnregisterFD(fd) )
+ return false;
+
+ // remove the handler if we don't need it any more
+ if ( !m_sets.HasFD(fd) )
{
- fd_set readset = m_readset;
- fd_set writeset = m_writeset;
- fd_set exeptset = m_exeptset;
- wxStopWatch sw;
- if ( ptv && timeout )
- sw.Start(ptv->tv_usec/10);
- ret = select(m_maxFD+1, &readset, &writeset, &exeptset, ptv);
- switch ( ret )
+ if ( fd == m_maxFD )
{
- // TODO: handle unix signals here
- case -1:
- if ( !timeout )
+ // need to find new max fd
+ m_maxFD = -1;
+ for ( wxFDIOHandlerMap::const_iterator it = m_handlers.begin();
+ it != m_handlers.end();
+ ++it )
+ {
+ if ( it->first > m_maxFD )
{
- // it doesn't make sense to remain here
- return;
+ m_maxFD = it->first;
}
+ }
+ }
+ }
- if ( ptv )
- {
- ptv->tv_sec = 0;
- ptv->tv_usec = timeout - sw.Time()*10;
- }
- break;
+ wxLogTrace(wxSelectDispatcher_Trace,
+ wxT("Removed fd %d, current max: %d"), fd, m_maxFD);
+ return true;
+}
+
+int wxSelectDispatcher::ProcessSets(const wxSelectSets& sets)
+{
+ int numEvents = 0;
+ for ( int fd = 0; fd <= m_maxFD; fd++ )
+ {
+ if ( !sets.HasFD(fd) )
+ continue;
+
+ wxFDIOHandler * const handler = FindHandler(fd);
+ if ( !handler )
+ {
+ wxFAIL_MSG( wxT("NULL handler in wxSelectDispatcher?") );
+ continue;
+ }
+
+ if ( sets.Handle(fd, *handler) )
+ numEvents++;
+ }
+
+ return numEvents;
+}
+
+int wxSelectDispatcher::DoSelect(wxSelectSets& sets, int timeout) const
+{
+ struct timeval tv,
+ *ptv;
+ if ( timeout != TIMEOUT_INFINITE )
+ {
+ ptv = &tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000)*1000;
+ }
+ else // no timeout
+ {
+ ptv = NULL;
+ }
- // timeout
- case 0:
- break;
+ int ret = sets.Select(m_maxFD + 1, ptv);
- default:
- ProcessSets(&readset, &writeset, &exeptset, m_maxFD+1);
- };
- } while (ret != 0);
+ // TODO: we need to restart select() in this case but for now just return
+ // as if timeout expired
+ if ( ret == -1 && errno == EINTR )
+ ret = 0;
+
+ return ret;
}
-// ----------------------------------------------------------------------------
-// wxSelectDispatcherModule
-// ----------------------------------------------------------------------------
+bool wxSelectDispatcher::HasPending() const
+{
+ wxSelectSets sets(m_sets);
+ return DoSelect(sets, 0) > 0;
+}
-class wxSelectDispatcherModule: public wxModule
+int wxSelectDispatcher::Dispatch(int timeout)
{
-public:
- bool OnInit() { wxLog::AddTraceMask(wxSelectDispatcher_Trace); return true; }
- void OnExit() { wxDELETE(wxSelectDispatcher::ms_instance); }
+ wxSelectSets sets(m_sets);
+ switch ( DoSelect(sets, timeout) )
+ {
+ case -1:
+ wxLogSysError(_("Failed to monitor I/O channels"));
+ return -1;
-private:
- DECLARE_DYNAMIC_CLASS(wxSelectDispatcherModule)
-};
+ case 0:
+ // timeout expired without anything happening
+ return 0;
-IMPLEMENT_DYNAMIC_CLASS(wxSelectDispatcherModule, wxModule)
+ default:
+ return ProcessSets(sets);
+ }
+}
+#endif // wxUSE_SELECT_DISPATCHER