Files
2022-12-13 12:44:04 +01:00

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;
}