586 lines
17 KiB
C++
Executable File
586 lines
17 KiB
C++
Executable File
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "cdevReactor.h"
|
|
|
|
// *****************************************************************************
|
|
// * This is the number of times that InitializeNetwork was called to perform
|
|
// * network initialization. The InitializeNetwork and corresponding
|
|
// * TerminateNetwork methods are wrappers to provide support for WIN32
|
|
// * WSAStartup and WSACleanup methods. They perform no actiual function on
|
|
// * UNIX platforms.
|
|
// *****************************************************************************
|
|
int cdevReactor::netInitCount = 0;
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::cdevReactor :
|
|
// * This is the constructor for the cdevReactor class. It takes no
|
|
// * arguments. It will set maxEntries to the value of the MAX_SIZE
|
|
// * variable and the size variable (the highest fd installed) will be set
|
|
// * to zero.
|
|
// *
|
|
// * The method will allocate space in the handlers array for the
|
|
// * cdevEventHandlers that will be installed during operation. The
|
|
// * read_set, write_set and except_set masks will be cleared. These masks
|
|
// * will be populated later as file descriptors are added to the
|
|
// * cdevReactor.
|
|
// *****************************************************************************
|
|
cdevReactor::cdevReactor ( void )
|
|
: maxEntries(cdevHandleSet::MAX_SIZE),
|
|
size (0),
|
|
handlers (NULL),
|
|
timers (NULL)
|
|
{
|
|
if(!netInitCount)
|
|
{
|
|
if(InitializeNetwork()==0) netInitCount++;
|
|
}
|
|
else netInitCount++;
|
|
|
|
handlers = (cdevEventHandler **)malloc((maxEntries+1)*sizeof(cdevEventHandler *));
|
|
memset(handlers, 0, sizeof(cdevEventHandler *)*(maxEntries+1));
|
|
read_set.reset();
|
|
write_set.reset();
|
|
except_set.reset();
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::~cdevReactor :
|
|
// * This is the destructor for the cdevReactor class. It wil first remove
|
|
// * all of the timer cdevEventHandlers from the timer array, it will then
|
|
// * remove all of the file-descriptor based cdevEventHandlers from the
|
|
// * handlers array. It will finish by deleteing the handlers array that
|
|
// * was allocated in the constructor.
|
|
// *****************************************************************************
|
|
cdevReactor::~cdevReactor ( void )
|
|
{
|
|
if(netInitCount && (-netInitCount)==0) TerminateNetwork();
|
|
|
|
while(timers!=NULL) removeHandler(timers);
|
|
for(int i=0; i<=maxEntries; i++)
|
|
{
|
|
if(handlers[i]!=NULL) removeHandler(handlers[i]);
|
|
}
|
|
delete handlers;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::calculateMask :
|
|
// * This method will walk through the handlers array and setup the read_set,
|
|
// * write_set and except_set masks.
|
|
// *****************************************************************************
|
|
void cdevReactor::calculateMask ( void )
|
|
{
|
|
int i;
|
|
read_set.reset();
|
|
write_set.reset();
|
|
except_set.reset();
|
|
size = 0;
|
|
|
|
for(i=0; i<=maxEntries; i++)
|
|
{
|
|
unsigned mask;
|
|
if(handlers[i]!=NULL)
|
|
{
|
|
mask = handlers[i]->getMask();
|
|
if(!(mask&cdevEventHandler::DONT_CALL))
|
|
{
|
|
if(mask&cdevEventHandler::READ_MASK)
|
|
{
|
|
read_set.set_bit(i);
|
|
size = i+1;
|
|
}
|
|
if(mask&cdevEventHandler::WRITE_MASK)
|
|
{
|
|
write_set.set_bit(i);
|
|
size = i+1;
|
|
}
|
|
if(mask&cdevEventHandler::EXCEPT_MASK)
|
|
{
|
|
except_set.set_bit(i);
|
|
size = i+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::calculateTimeout :
|
|
// * This method will walk through the timers and determine the duration of
|
|
// * time that the cdevReactor should wait for events on the file
|
|
// * descriptors before automatically terminating.
|
|
// *****************************************************************************
|
|
int cdevReactor::calculateTimeout ( cdevTime defaultPeriod, struct timeval &timeout )
|
|
{
|
|
if(defaultPeriod==cdevTime(0,0)) timeout.tv_sec=(timeout.tv_usec=0);
|
|
else {
|
|
cdevTime now;
|
|
cdevTime target;
|
|
cdevEventHandler * timer;
|
|
|
|
if(defaultPeriod<cdevTime(0,0)) defaultPeriod.setTime(60, 0);
|
|
|
|
now.setTime();
|
|
target.setTime(now+defaultPeriod);
|
|
|
|
for(timer=timers; timer!=NULL && target>now; timer=timer->getNext())
|
|
{
|
|
cdevTime nextTimeout = timer->getNextTimeout();
|
|
|
|
if(!(timer->getMask()&cdevEventHandler::DONT_CALL) &&
|
|
(double)nextTimeout > 0.0)
|
|
{
|
|
if(nextTimeout<target) target = nextTimeout;
|
|
}
|
|
}
|
|
|
|
if(target<=now) timeout.tv_sec=(timeout.tv_usec=0);
|
|
else timeout=target-now+(cdevTime)0.01;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::checkHandlers :
|
|
// * This method is used to remove any cdevEventHandlers that have dead
|
|
// * sockets from the cdevReactor prior to beginning I/O operations. The
|
|
// * method calls select with all of the file handles installed. If select
|
|
// * returns a -1, then each of the file handles is checked independently.
|
|
// * When the bad handler is identified, its cdevEventHandler object will
|
|
// * be elliminated.
|
|
|
|
// * This method returns the number of cdevEventHandlers that were deleted.
|
|
// *****************************************************************************
|
|
int cdevReactor::checkHandlers ( void )
|
|
{
|
|
int i;
|
|
int eventCnt = 0;
|
|
struct timeval t;
|
|
|
|
read_set.reset();
|
|
write_set.reset();
|
|
except_set.reset();
|
|
t.tv_sec = 0;
|
|
t.tv_usec = 0;
|
|
|
|
for(i=0; i<maxEntries; i++)
|
|
{
|
|
if(handlers[i]!=NULL)
|
|
{
|
|
read_set.set_bit(i);
|
|
size = i+1;
|
|
}
|
|
}
|
|
|
|
if(cdevSelect(size, read_set, write_set, except_set, &t)<0)
|
|
{
|
|
for(i=0; i<maxEntries; i++)
|
|
{
|
|
if(handlers[i]!=NULL)
|
|
{
|
|
read_set.set_bit(i);
|
|
|
|
if(cdevSelect(i, read_set, write_set, except_set, &t)<0)
|
|
{
|
|
if(handlers[i]->handleSignal()<0)
|
|
{
|
|
removeHandler(handlers[i]);
|
|
eventCnt++;
|
|
}
|
|
}
|
|
read_set.clr_bit(i);
|
|
}
|
|
}
|
|
}
|
|
return eventCnt;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::registerHandler :
|
|
// * This method is called to register a file-descriptor based
|
|
// * cdevEventHandler with the reactor. It will first check the validity
|
|
// * of the handler and its file descriptor. It will then determine if
|
|
// * another handler already occupies the position associated with the
|
|
// * specified handle.
|
|
// *
|
|
// * If the handler is valid, it will install it in the handlers array and
|
|
// * will add it to the read_set, write_set and except_set masks as
|
|
// * required. The size variable will be incremented if the new handler
|
|
// * has the highest file-descriptor in the array.
|
|
// *****************************************************************************
|
|
int cdevReactor::registerHandler ( cdevEventHandler * handler, unsigned mask )
|
|
{
|
|
int fd = -1;
|
|
REACTOR_RESULT result = SUCCESS;
|
|
|
|
if(handler == NULL)
|
|
{
|
|
result = INVALID_HANDLER;
|
|
}
|
|
else if(handler->getReactor()!=NULL && handler->getReactor()!=this)
|
|
{
|
|
result = UNKNOWN_HANDLER;
|
|
}
|
|
else if((fd = handler->getHandle())<=0)
|
|
{
|
|
result = INVALID_HANDLE;
|
|
}
|
|
else if(fd<maxEntries && handlers[handler->getHandle()]!=NULL)
|
|
{
|
|
result = HANDLE_EXISTS;
|
|
}
|
|
else {
|
|
if(fd>=maxEntries)
|
|
{
|
|
int oldMaxEntries = maxEntries;
|
|
maxEntries = fd+1;
|
|
handlers = (cdevEventHandler **)realloc(handlers, (maxEntries+1) * sizeof(cdevEventHandler *));
|
|
memset(&handlers[oldMaxEntries], 0, sizeof(cdevEventHandler *)*((maxEntries+1)-oldMaxEntries));
|
|
}
|
|
handler->setReactor(this);
|
|
handler->setMask (mask);
|
|
handlers[fd] = handler;
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::removeHandler :
|
|
// * This method allows the caller to remove an cdevEventHandler from the
|
|
// * cdevReactor and delete it. The method will call the extractHandler
|
|
// * method to remove the cdevEventHandler from the cdevReactor after which
|
|
// * the handler will be deleted.
|
|
// *****************************************************************************
|
|
int cdevReactor::removeHandler ( cdevEventHandler * handler )
|
|
{
|
|
REACTOR_RESULT result = INVALID_HANDLER;
|
|
if(handler!=NULL)
|
|
{
|
|
extractHandler(handler);
|
|
delete handler;
|
|
result = SUCCESS;
|
|
}
|
|
return (int)result;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::removeHandler :
|
|
// * This method will remove the specified handler using the file descriptor
|
|
// * provided by the caller.
|
|
// *****************************************************************************
|
|
int cdevReactor::removeHandler ( int fd )
|
|
{
|
|
REACTOR_RESULT result = SUCCESS;
|
|
|
|
if(fd>0 && fd<=maxEntries)
|
|
{
|
|
result = (REACTOR_RESULT)removeHandler(handlers[fd]);
|
|
}
|
|
else result = INVALID_HANDLE;
|
|
|
|
return (int) result;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::extractHandler :
|
|
// * This method will remove the specified cdevEventHandler from the
|
|
// * cdevReactor, however, it will not delete the cdevEventHandler when
|
|
// * finished.
|
|
// *****************************************************************************
|
|
int cdevReactor::extractHandler ( cdevEventHandler * handler )
|
|
{
|
|
int fd = -1;
|
|
REACTOR_RESULT result = UNKNOWN_HANDLER;
|
|
|
|
if(handler == NULL) result = INVALID_HANDLER;
|
|
else {
|
|
cdevEventHandler * currTimer = timers;
|
|
cdevEventHandler * prevTimer = NULL;
|
|
|
|
while(currTimer!=NULL && currTimer!=handler)
|
|
{
|
|
prevTimer = currTimer;
|
|
currTimer = prevTimer->getNext();
|
|
}
|
|
|
|
if(currTimer)
|
|
{
|
|
if(prevTimer) prevTimer->setNext(currTimer->getNext());
|
|
else timers = currTimer->getNext();
|
|
result = SUCCESS;
|
|
}
|
|
|
|
if((fd = handler->getHandle())>=0 &&
|
|
fd<=maxEntries &&
|
|
handlers[fd]==handler)
|
|
{
|
|
handlers[fd] = NULL;
|
|
result = SUCCESS;
|
|
}
|
|
else for(fd=0; fd<=maxEntries; fd++)
|
|
{
|
|
if(handlers[fd]==handler)
|
|
{
|
|
handlers[fd] = NULL;
|
|
result = SUCCESS;
|
|
}
|
|
}
|
|
|
|
if(result==SUCCESS)
|
|
{
|
|
handler->setMask(0);
|
|
handler->setReactor(NULL);
|
|
}
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::getHandler :
|
|
// * This method allows the caller to retrieve the cdevEventHandler
|
|
// * associated with a specific file descriptor.
|
|
// *****************************************************************************
|
|
int cdevReactor::getHandler ( int fd, cdevEventHandler * & handler )
|
|
{
|
|
REACTOR_RESULT result = SUCCESS;
|
|
|
|
handler = NULL;
|
|
|
|
if(fd<=0 || fd>maxEntries) result = INVALID_HANDLE;
|
|
else if((handler = handlers[fd])==NULL) result = INVALID_HANDLE;
|
|
|
|
return (int) result;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::registerTimer :
|
|
// * This method will install a timer cdevEventHandler in the timers array.
|
|
// * The handleTimeout method of this class will be called each time the
|
|
// * timer expires. The timer's handleTimeout method will be called
|
|
// * immediately, and then the timer will be set to expire at the next
|
|
// * time period.
|
|
// *****************************************************************************
|
|
int cdevReactor::registerTimer ( cdevEventHandler * timer )
|
|
{
|
|
REACTOR_RESULT result = SUCCESS;
|
|
|
|
if(timer==NULL)
|
|
{
|
|
result = INVALID_HANDLER;
|
|
}
|
|
else if(timer->getReactor()!=NULL && timer->getReactor()!=this)
|
|
{
|
|
result = UNKNOWN_HANDLER;
|
|
}
|
|
else if((double)timer->getTimeoutRate()<=0.0)
|
|
{
|
|
result = INVALID_TIMEOUT;
|
|
}
|
|
else {
|
|
timer->setReactor(this);
|
|
timer->resetTimer();
|
|
timer->setNext(timers);
|
|
timers = timer;
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::cancelTimer :
|
|
// * This method will remove the timer from the array of timers that are
|
|
// * being serviced by the reactor, however, it will NOT delete the
|
|
// * cdevEventHandler. Additionally, if the handler is also being used
|
|
// * for file-descriptor based operations - it will be not be removed from
|
|
// * that array.
|
|
// *
|
|
// * The user is responsible for deleteing the cdevEventHandler if it will
|
|
// * no longer be needed.
|
|
// *****************************************************************************
|
|
int cdevReactor::cancelTimer ( cdevEventHandler * timer )
|
|
{
|
|
REACTOR_RESULT result = SUCCESS;
|
|
|
|
if(timer==NULL) result = INVALID_HANDLER;
|
|
else
|
|
{
|
|
cdevEventHandler * currTimer = timers;
|
|
cdevEventHandler * prevTimer = NULL;
|
|
|
|
while(currTimer!=NULL && currTimer!=timer)
|
|
{
|
|
prevTimer = currTimer;
|
|
currTimer = prevTimer->getNext();
|
|
}
|
|
|
|
if(currTimer)
|
|
{
|
|
if(prevTimer) prevTimer->setNext(currTimer->getNext());
|
|
else timers = currTimer->getNext();
|
|
currTimer->setNext(NULL);
|
|
}
|
|
else result = UNKNOWN_HANDLER;
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::handleFileEvent :
|
|
// * This method will process the events that have occurred on a single
|
|
// * file descriptor. The caller must provide two arguments...
|
|
// *
|
|
// * fd_set * fds : The fd_set to be checked
|
|
// * REACTOR_EVENT event : The type of event being processed...
|
|
// * INPUT, OUTPUT, EXCEPTION, or SIGNAL.
|
|
// *
|
|
// * This method will return the number of events that have been processed
|
|
// * to completion. Note that if a cdevEventHandler returns a value greater
|
|
// * than 0, it will be called again after all other ready file descriptors
|
|
// * have been called - therefore, its occurance will not be included in the
|
|
// * number of events returned by this method.
|
|
// *****************************************************************************
|
|
int cdevReactor::handleFileEvent ( cdevHandleSet * fds, REACTOR_EVENT event)
|
|
{
|
|
int result = 0;
|
|
int eventCnt = 0;
|
|
int i;
|
|
|
|
for(i=0; i<=size; i++)
|
|
{
|
|
if(fds->is_set(i))
|
|
{
|
|
if(handlers[i])
|
|
{
|
|
if(event==INPUT) result = handlers[i]->handleInput();
|
|
else if(event==OUTPUT) result = handlers[i]->handleOutput();
|
|
else if(event==EXCEPTION) result = handlers[i]->handleExcept();
|
|
else if(event==SIGNAL) result = handlers[i]->handleSignal();
|
|
|
|
if(result<=0)
|
|
{
|
|
if(result<0) removeHandler(handlers[i]);
|
|
eventCnt++;
|
|
fds->clr_bit(i);
|
|
}
|
|
}
|
|
else eventCnt++;
|
|
}
|
|
}
|
|
|
|
return eventCnt;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * cdevReactor::handleEvents :
|
|
// * This method is called to allow the cdevReactor to wait for events to
|
|
// * occur on the cdevEventHandler objects. This method will process events
|
|
// * until the specified time period has expired - if a negative time period
|
|
// * (or no time period) has been specified, then it will process messages
|
|
// * until the next event occurs.
|
|
// *
|
|
// * The flags parameter was added to allow the caller to specify either
|
|
// * UNTIL_TIMEOUT or UNTIL_EVENT. If the caller specifies UNTIL_TIMEOUT,
|
|
// * the cdevReactor will continue to handle events until the time period
|
|
// * has expired. If the caller specifies UNTIL_EVENT, the cdevReactor will
|
|
// * return after the first SOCKET-BASED EVENT occurs or after the time
|
|
// * expires, whichever comes first.
|
|
// *
|
|
// * Note: If the cdevEventHandler may return a -1, 0, or 1. These return
|
|
// * codes will have the following effect.
|
|
// *
|
|
// * -1 : The cdevEventHandler will be removed from the cdevReactor
|
|
// * and deleted.
|
|
// * 0 : The bit associated with the cdevEventHandler will be
|
|
// * cleared and processing will continue.
|
|
// * 1 : The bit associated with the cdevEventHandler will not be
|
|
// * cleared and it will be called to process more data after
|
|
// * after all of the subsequent cdevEventHandlers have been
|
|
// * called - this allows the cdevEventHandler in a lengthy
|
|
// * process to yeild time back for processing other events.
|
|
// *****************************************************************************
|
|
int cdevReactor::handleEvents ( cdevTime period, int flags )
|
|
{
|
|
int result = 0;
|
|
int finished = 0;
|
|
cdevTime startTime, currTime;
|
|
struct timeval timeout;
|
|
|
|
startTime.setTime();
|
|
|
|
do
|
|
{
|
|
int i;
|
|
|
|
calculateMask ();
|
|
calculateTimeout(period, timeout);
|
|
|
|
result=cdevSelect(size, read_set, write_set, except_set, &timeout);
|
|
|
|
if(result<0)
|
|
{
|
|
finished = 1;
|
|
checkHandlers();
|
|
}
|
|
else {
|
|
if(result>0)
|
|
{
|
|
int cnt = 0;
|
|
|
|
while(cnt<result)
|
|
{
|
|
cnt+=handleFileEvent(&read_set, INPUT);
|
|
cnt+=handleFileEvent(&write_set, OUTPUT);
|
|
cnt+=handleFileEvent(&except_set, EXCEPTION);
|
|
}
|
|
if(flags==UNTIL_EVENT && cnt>0) finished = 1;
|
|
}
|
|
|
|
if(timers!=NULL)
|
|
{
|
|
cdevEventHandler * timer=timers;
|
|
|
|
currTime.setTime();
|
|
|
|
while(timer!=NULL)
|
|
{
|
|
cdevTime nextTimeout = timer->getNextTimeout();
|
|
|
|
if((timer->getMask()&cdevEventHandler::DONT_CALL)==0 &&
|
|
(double)nextTimeout>0.0 && nextTimeout<=currTime)
|
|
{
|
|
if(timer->handleTimeout()<0)
|
|
{
|
|
cdevEventHandler *temp = timer->getNext();
|
|
removeHandler(timer);
|
|
timer = temp;
|
|
}
|
|
else
|
|
{
|
|
timer->resetTimer();
|
|
timer = timer->getNext();
|
|
}
|
|
currTime.setTime();
|
|
}
|
|
else timer = timer->getNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
currTime.setTime();
|
|
period = period-(currTime-startTime);
|
|
} while(!finished && period>cdevTime(0,0));
|
|
|
|
return result;
|
|
}
|