#include #include #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(defaultPeriodnow; timer=timer->getNext()) { cdevTime nextTimeout = timer->getNextTimeout(); if(!(timer->getMask()&cdevEventHandler::DONT_CALL) && (double)nextTimeout > 0.0) { if(nextTimeouthandleSignal()<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(fdgetHandle()]!=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(cnt0) 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; }