poco/FSM/samples/TCP/Eventloop.cpp
2016-07-18 14:34:53 +02:00

622 lines
17 KiB
C++

//
// The contents of this file are subject to the Mozilla Public
// License Version 1.1 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy
// of the License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
// implied. See the License for the specific language governing
// rights and limitations under the License.
//
// The Original Code is State Machine Compiler (SMC).
//
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright (C) 2000 - 2007. Charles W. Rapp.
// All Rights Reserved.
//
// Contributor(s):
//
// Name
// Eventloop.h
//
// Description
// Encapsulates the select-based event loop and timer table.
//
// RCS ID
// $Id: Eventloop.cpp,v 1.6 2008/02/04 12:45:07 fperrad Exp $
//
// CHANGE LOG
// $Log: Eventloop.cpp,v $
// Revision 1.6 2008/02/04 12:45:07 fperrad
// fix build on linux (gcc 4.1.3)
//
// Revision 1.5 2007/12/28 12:34:40 cwrapp
// Version 5.0.1 check-in.
//
// Revision 1.4 2005/05/28 13:31:18 cwrapp
// Updated C++ examples.
//
// Revision 1.0 2003/12/14 19:35:40 charlesr
// Initial revision
//
#include "Eventloop.h"
#include <errno.h>
#if !defined(WIN32)
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
const static long USECS_PER_SEC = 1000000;
const static long USECS_PER_MSEC = 1000;
const static int WIN32_MAX_SOCKET = 1024;
//---------------------------------------------------------------
// Eventloop() (Public)
// Default constructor.
//
Eventloop::Eventloop()
: _continueRunning(1),
_maxFD(-1),
_fdCount(0),
_timerTable(NULL)
{
#if !defined(WIN32)
rlimit limit;
#endif
FD_ZERO(&_fdSet);
// Allocate the file descriptor callback table. First, find
// out the max file descriptor value.
#if defined(WIN32)
_fdTable = new InputListener*[WIN32_MAX_SOCKET];
(void) memset(_fdTable,
0,
(FD_SETSIZE * sizeof(InputListener*)));
#else
(void) getrlimit(RLIMIT_NOFILE, &limit);
_fdTable = new InputListener*[limit.rlim_cur];
(void) memset(_fdTable,
0,
(limit.rlim_cur * sizeof(InputListener*)));
#endif
return;
} // end of Eventloop::Eventloop()
//---------------------------------------------------------------
// ~Eventloop() (Public)
// Destructor.
//
Eventloop::~Eventloop()
{
Eventloop::Timer *timerIt,
*nextTimer;
// Just in case the event loop is still running.
_continueRunning = 0;
if (_fdTable != NULL)
{
delete[] _fdTable;
_fdTable = NULL;
}
for (timerIt = _timerTable;
timerIt != NULL;
timerIt = nextTimer)
{
nextTimer = timerIt->getNext();
timerIt->setNext(NULL);
delete timerIt;
}
return;
} // end of Eventloop::~Eventloop()
//---------------------------------------------------------------
// start() (Public)
// Start the event loop running.
//
int Eventloop::start()
{
timeval timeout,
currTime,
*timeoutPtr;
Timer *timer;
fd_set readSet;
int i,
fdCount,
retcode;
TimerListener *listener;
char *timerName;
_continueRunning = 1;
retcode = 0;
while (_continueRunning == 1)
{
// Figure out how long to wait. If there are no timers,
// then set timeoutPtr to NULL and wait forever.
// Otherwise, put the timeout in the timeval struct and
// point timeoutPtr to it.
if (_timerTable == NULL)
{
timeoutPtr = NULL;
}
else
{
// Get the current time and figure out how long until
// the next timeout.
gettime(currTime);
// If we have passed the current timeout value, then
// don't sleep at all.
if (*_timerTable < currTime)
{
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
else
{
timeout.tv_sec =
(_timerTable->getExpiration()).tv_sec - currTime.tv_sec;
timeout.tv_usec =
(_timerTable->getExpiration()).tv_usec - currTime.tv_usec;
// If the microseconds are less than 0, then subtract
// a second and add 1,000,000 microseconds.
if (timeout.tv_usec < 0)
{
--(timeout.tv_sec);
timeout.tv_usec += USECS_PER_SEC;
}
}
timeoutPtr = &timeout;
}
// Since select modifies the FD sets, pass in a copy.
FD_ZERO(&readSet);
for (i = 0; i < _maxFD; ++i)
{
if (FD_ISSET(i, &_fdSet))
{
#if defined(WIN32)
FD_SET((u_int) i, &readSet);
#else
FD_SET(i, &readSet);
#endif
}
}
if ((fdCount = select(_maxFD, &readSet, NULL, NULL, timeoutPtr)) < 0)
{
// If select() was interrupted, ignore the error.
if (errno != EINTR)
{
_continueRunning = 0;
retcode = errno;
}
}
else
{
// Issue read events first.
for (i = 0; i < _maxFD && fdCount > 0; ++i)
{
if (FD_ISSET(i, &readSet))
{
--fdCount;
if (_fdTable[i] != NULL)
{
_fdTable[i]->handleReceive(i);
}
}
}
// Issue timeouts.
gettime(currTime);
while (_timerTable != NULL &&
*_timerTable <= currTime)
{
// Remove the timer from the timer table *before*
// issuing the callback 'cause the callback may
// set this same timer again before returning
// (and the set will fail if the timer is already
// in the table).
timer = _timerTable;
_timerTable = timer->getNext();
timer->setNext(NULL);
// Save away the necessary timer info.
listener = &(timer->getListener());
timerName = new char[strlen(timer->getName()) + 1];
strcpy(timerName, timer->getName());
// Now delete the defunct timer.
delete timer;
listener->handleTimeout(timerName);
delete[] timerName;
}
}
}
return(retcode);
} // end of Eventloop::start()
//---------------------------------------------------------------
// stop() (Public)
// Stop the event loop.
//
void Eventloop::stop()
{
_continueRunning = 0;
return;
} // end of Eventloop::stop()
//---------------------------------------------------------------
// addFD(int, InputListener&) (Public)
// Start watching a file descriptor and route the callback to the
// given connection.
//
void Eventloop::addFD(int fd, InputListener& listener)
{
if (!FD_ISSET(fd, &_fdSet))
{
#if defined(WIN32)
FD_SET((u_int) fd, &_fdSet);
#else
FD_SET(fd, &_fdSet);
#endif
++_fdCount;
}
_fdTable[fd] = &listener;
if (fd >= _maxFD)
{
_maxFD = fd + 1;
}
return;
} // end of Eventloop::addFD(int, InputListener&)
//---------------------------------------------------------------
// removeFD(int) (Public)
// Stop watching a file descriptor.
//
void Eventloop::removeFD(int fd)
{
int i;
#if defined(WIN32)
FD_CLR((u_int) fd, &_fdSet);
#else
FD_CLR(fd, &_fdSet);
#endif
_fdTable[fd] = NULL;
--_fdCount;
// Are we watching any file descriptors?
if (_fdCount == 0)
{
_maxFD = -1;
}
// Are we removing the max fd?
else if (fd == (_maxFD - 1))
{
// Yes. Then look for the next max fd.
for (i = (fd -1); i >= 0; --i)
{
if (FD_ISSET(i, &_fdSet))
{
_maxFD = i + 1;
break;
}
}
}
return;
} // end of Eventloop::removeFD(int)
//---------------------------------------------------------------
// startTimer(const char*, unsigned long, TimerListener&) (Public)
// Start the named timer.
//
void Eventloop::startTimer(const char *name,
long duration,
TimerListener& listener)
{
timeval expiration;
Eventloop::Timer *timer,
**timerIt;
// Get the current time and add the duration to it.
gettime(expiration);
expiration.tv_usec += duration * USECS_PER_MSEC;
if (expiration.tv_usec >= USECS_PER_SEC)
{
expiration.tv_sec += (expiration.tv_usec / USECS_PER_SEC);
expiration.tv_usec = (expiration.tv_usec % USECS_PER_SEC);
}
// Check if this timer already exists. If yes, then remove the
// timer from the list and update its duration and expiration.
timer = NULL;
for (timerIt = &_timerTable;
*timerIt != NULL;
timerIt = &((*timerIt)->_next))
{
if (strcmp((*timerIt)->getName(), name) == 0 &&
&((*timerIt)->getListener()) == &listener)
{
timer = *timerIt;
*timerIt = timer->getNext();
timer->setNext(NULL);
timer->setDuration(duration);
timer->setExpiration(expiration);
break;
}
}
if (timer == NULL)
{
timer = new Timer(name, duration, expiration, listener);
}
// Find out where to put this timer in the list.
for (timerIt = &_timerTable;
*timerIt != NULL && **timerIt <= expiration;
timerIt = &((*timerIt)->_next))
;
timer->setNext(*timerIt);
*timerIt = timer;
return;
} // end of Eventloop::startTimer(const char*, long, TimerListener&)
//---------------------------------------------------------------
// stopTimer(const char*, const TimerListener&) (Public)
// Stop the named timer.
//
void Eventloop::stopTimer(const char *name,
const TimerListener& listener)
{
Eventloop::Timer **timerIt,
*timer;
for (timerIt = &_timerTable;
*timerIt != NULL;
timerIt = &((*timerIt)->_next))
{
if (strcmp((*timerIt)->getName(), name) == 0 &&
&((*timerIt)->getListener()) == &listener)
{
timer = *timerIt;
*timerIt = timer->getNext();
timer->setNext(NULL);
delete timer;
break;
}
}
return;
} // end of Eventloop::stopTimer(const char*, const TimerListener&)
//---------------------------------------------------------------
// doesTimerExist(const char*) const (Public)
// Return true if the named timer exists.
//
bool Eventloop::doesTimerExist(const char *name) const
{
Eventloop::Timer **timerIt;
bool retval;
for (timerIt = const_cast<Eventloop::Timer**>(&_timerTable),
retval = false;
*timerIt != NULL && retval == false;
timerIt = &((*timerIt)->_next))
{
if (strcmp((*timerIt)->getName(), name) == 0)
{
retval = true;
}
}
return (retval);
} // end of Eventloop::doesTimerExist(const char*) const
//---------------------------------------------------------------
// Timer(const char*, long, const timeval&, TimerListener&)
// (Public)
// Constructor.
//
Eventloop::Timer::Timer(const char *name,
long duration,
const timeval& expiration,
TimerListener& listener)
: _name(NULL),
_duration(duration),
_listener(listener),
_next(NULL)
{
_name = new char[strlen(name) + 1];
(void) strcpy(_name, name);
(void) memcpy(&_expiration, &expiration, sizeof(_expiration));
} // end of Eventloop::Timer::Timer(...)
//---------------------------------------------------------------
// ~Timer() (Public)
// Destructor.
//
Eventloop::Timer::~Timer()
{
if (_name != NULL)
{
delete[] _name;
_name = NULL;
}
return;
} // end of Eventloop::Timer::~Timer()
//---------------------------------------------------------------
// operator<(const timeval&) (Public)
// Is the expiration time less than the given time?
//
int Eventloop::Timer::operator<(const timeval& time)
{
return(_expiration.tv_sec < time.tv_sec ||
(_expiration.tv_sec == time.tv_sec &&
_expiration.tv_usec < time.tv_usec));
} // end of Eventloop::Timer::operator<(const timeval&)
//---------------------------------------------------------------
// operator<=(const timeval&) (Public)
// Is the expiration time less than or equal to the given time?
//
int Eventloop::Timer::operator<=(const timeval& time)
{
return(_expiration.tv_sec < time.tv_sec ||
(_expiration.tv_sec == time.tv_sec &&
_expiration.tv_usec <= time.tv_usec));
} // end of Eventloop::Timer::operator<=(const timeval&)
//---------------------------------------------------------------
// operator==(const timeval&) (Public)
// Is the expiration time equal to the given time?
//
int Eventloop::Timer::operator==(const timeval& time)
{
return(_expiration.tv_sec == time.tv_sec &&
_expiration.tv_usec == time.tv_usec);
} // end of Eventloop::Timer::operator==(const timeval&)
//---------------------------------------------------------------
// operator>=(const timeval&) (Public)
// Is the expiration time greater than or equal to the given
// time?
//
int Eventloop::Timer::operator>=(const timeval& time)
{
return(_expiration.tv_sec > time.tv_sec ||
(_expiration.tv_sec == time.tv_sec &&
_expiration.tv_usec >= time.tv_usec));
} // end of Eventloop::Timer::operator>=(const timeval&)
//---------------------------------------------------------------
// operator>(const timeval&) (Public)
// Is the expiration time greater than the given time?
//
int Eventloop::Timer::operator>(const timeval& time)
{
return(_expiration.tv_sec > time.tv_sec ||
(_expiration.tv_sec == time.tv_sec &&
_expiration.tv_usec > time.tv_usec));
} // end of Eventloop::Timer::operator>(const timeval&)
//---------------------------------------------------------------
// getName() const (Public)
// Return the timer's name.
//
const char* Eventloop::Timer::getName() const
{
return(_name);
} // end of Eventloop::Timer::getName() const
//---------------------------------------------------------------
// getDuration() const (Public)
// Return the timer's duration in milliseconds.
//
long Eventloop::Timer::getDuration() const
{
return(_duration);
} // end of Eventloop::Timer::getDuration() const
//---------------------------------------------------------------
// setDuration(long) (Public)
// Set the timer's duration.
//
void Eventloop::Timer::setDuration(long duration)
{
_duration = duration;
return;
} // end of Eventloop::Timer::setDuration(long)
//---------------------------------------------------------------
// getExpiration() const (Public)
// Return the timer's expiration.
//
const timeval& Eventloop::Timer::getExpiration() const
{
return(_expiration);
} // end of Eventloop::Timer::getExpiration() const
//---------------------------------------------------------------
// setExpiration(const timeval&) (Public)
// Set the timer's expiration.
//
void Eventloop::Timer::setExpiration(const timeval& expiration)
{
(void) memcpy(&_expiration, &expiration, sizeof(_expiration));
return;
} // end of Eventloop::Timer::setExpiration(const timeval&)
//---------------------------------------------------------------
// getListener() const (Public)
// Return timer's callback connection object.
//
TimerListener& Eventloop::Timer::getListener() const
{
return(_listener);
} // end of Eventloop::Timer::getListener() const
//---------------------------------------------------------------
// getNext() const (Public)
// Return the next pointer.
//
Eventloop::Timer* Eventloop::Timer::getNext() const
{
return(_next);
} // end of Eventloop::Timer::getNext() const
//---------------------------------------------------------------
// setNext(Timer*) (Public)
// Set the next pointer.
//
void Eventloop::Timer::setNext(Eventloop::Timer *timer)
{
_next = timer;
return;
} // end of Eventloop::Timer::setNext(Eventloop::Timer*)
//---------------------------------------------------------------
// gettime(timeval&) const (Private)
// Get the current time.
//
void Eventloop::gettime(timeval& timeval) const
{
#if defined(WIN32)
_timeb currTime;
_ftime(&currTime);
timeval.tv_sec = currTime.time;
timeval.tv_usec = currTime.millitm * USECS_PER_MSEC;
#else
(void) gettimeofday(&timeval, NULL);
#endif
return;
} // end of Eventloop::gettime(timeval&) const