// -*- c++ -*- //------------------------------------------------------------------------------ // Reactor.h //------------------------------------------------------------------------------ // Copyright (C) 1997-2002,2005 Vladislav Grinchenko // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. //------------------------------------------------------------------------------ // Created: 05/25/1999 //------------------------------------------------------------------------------ #ifndef REACTOR_H #define REACTOR_H #include // select(2) #include #if !defined(WIN32) # include // getrlimit(2) #endif #include "assa/EventHandler.h" #include "assa/Singleton.h" #include "assa/MaskSet.h" #include "assa/TimerQueue.h" #include "assa/TimerCountdown.h" namespace ASSA { /** @file Reactor.h An implementation of Reactor pattern. This class takes after Reactor pattern described in "An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events" by D.C. Schmidt. Design of it is highly influenced by ACE's own implementation of Reactor class as well as by InterViews V 3.1 from Stanford University. Reactor reads data from a file descriptor, writes data to a file descriptor, handles an I/O exception on a file descriptor, or handles a timer's expiration. Once the user has requested the Reactor to attach EventHandler to a file descriptor or a timer, Reactor will automatically notify the EventHandler when the file descriptor's I/O condition changes to the timer expires. Most of the time, however, event processing is shared with some other event processor, such as X event loop routine of Gtk+ or another communication framework such as CORBA. In this case, timed event loop is used. This can be achieved by calling Reactor::waitForEvents (TimeVal* tv_). */ class Reactor { public: /// Constructor. Reactor (); /// Destructor. ~Reactor(); /** Register Timer Event handler with Reactor. Reactor will dispatch appropriate callback when event of EventType is received. @param eh_ Pointer to the EventHandler @param tv_ Timeout value @param name_ Name of the timer @return Timer ID that can be used to cancel timer and find out its name. */ TimerId registerTimerHandler (EventHandler* eh_, const TimeVal& tv_, const std::string& name_ = ""); /** Register I/O Event handler with Reactor. Reactor will dispatch appropriate callback when event of EventType is received. @param eh_ Pointer to the EventHandler @param fd_ File descriptor @param et_ Event Type @return true if success, false if error */ bool registerIOHandler (EventHandler* eh_, handler_t fd_, EventType et_ = RWE_EVENTS); /** Remove Event handler from reactor for either all I/O events or timeout event or both. If et_ is TIMEOUT_EVENT, all timers associated with Event Handler eh_ will be removed. @param eh_ Pointer to the EventHandler @param et_ Event Type to remove. Default will remove Event Handler for all events. @return true if success, false if wasn't registered for any events. */ bool removeHandler (EventHandler* eh_, EventType et_ = ALL_EVENTS); /** Remove Timer event from the queue. This removes particular event. @param id_ Timer Id returned by registerTimer. @return true if timer found and removed; false otherwise */ bool removeTimerHandler (TimerId id_); /** Remove IO Event handler from reactor. This will remove handler from receiving all I/O events. @param fd_ File descriptor @return true on success, false if fd_ is out of range */ bool removeIOHandler (handler_t fd_); /// Main waiting loop that blocks indefinitely processing events. void waitForEvents (void); /** Wait for events for time specified. Passing NULL replicates behavior of waitForEvents(void). Passing tv_ {0, 0} will cause non-blocking polling for all events. This method blocks up to tv_ time interval processing event. If an event occurs, it will process event(s) and return. tv_ time is adjusted by substracting time spent in event processing. @param tv_ [RW] is time to wait for. */ void waitForEvents (TimeVal* tv_); /** Stop Reactor's activity. This effectively removes all handlers from under Reactor's supervision. As of now, there is no way to re-activate the Reactor. This method is typically called from method other then EventHandler::signal_handler(). EventHandler::handle_read () is a good candidate. Calling it from EventHandler::handle_close () will most likely cause an infinite loop of recursive calls. */ void stopReactor (void); /** Deactivate Reactor. This function sets internal flag which notifies Reactor's internal event handling loop to abort its activity. It is mostly used when a *slow* system call is interrupted by the signal handler. The system call will be restarted by OS after control returns from the signal handler. Signal handler (GenServer::handle_signal()) should call this method to delay Reactor's deactivation. */ void deactivate (void); private: Reactor (const Reactor&); /// no cloning Reactor& operator= (const Reactor&); /// no cloning private: typedef std::map Fd2Eh_Map_Type; typedef Fd2Eh_Map_Type::iterator Fd2Eh_Map_Iter; private: /// Adjust maxfdp1 in a portable way (win32 ignores masfd alltogether). void adjust_maxfdp1 (handler_t fd_, handler_t rmax_, handler_t wmax_, handler_t emax_); /// Handle error in select(2) loop appropriately. bool handleError (void); /** Notify all EventHandlers registered on respecful events occured. @param minimum_ number of file descriptors ready. */ bool dispatch (int minimum_); /// Return number of file descriptors ready accross all sets. int isAnyReady (void); /** Check mask for bad file descriptors. @return true if any fd(s) were found and removed; false otherwise */ bool checkFDs (void); /** Call handler's callback and, if callback returns negative value, remove it from the Reactor. */ void dispatchHandler ( FdSet& mask_, Fd2Eh_Map_Type& fdSet_, EH_IO_Callback callback_); /** Calculate closest timeout. If TimerQueue is not empty, then return smallest of maxtimeout and first in the queue. Otherwise, return maxtimeout. @param maxwait_ (in) how long we are expected to wait for event(s). @param howlong_ (out) how long we are going to wait. */ void calculateTimeout (TimeVal*& howlong_, TimeVal* maxwait_); private: /** Max number of open files per process. This is the soft limit enforced by the kernel. It can be obtained/manipulated from the shell with ulimit/limit utilities, but may not exceed the hard limit. */ int m_fd_setsize; /** * Max file descriptor number (in all sets) plus 1. * This value is ignored by WIN32 implementation of select() */ handler_t m_maxfd_plus1; /// Flag that indicates whether Reactor is active or had been stopped. bool m_active; /// Event handlers awaiting on READ_EVENT Fd2Eh_Map_Type m_readSet; /// Event handlers awaiting on WRITE_EVENT Fd2Eh_Map_Type m_writeSet; /// Event handlers awaiting on EXCEPT_EVENT Fd2Eh_Map_Type m_exceptSet; /// Handlers to wait for event on MaskSet m_waitSet; /// Handlers that are ready for processing MaskSet m_readySet; /// The queue of Timers. TimerQueue m_tqueue; }; //----------------------------------------------------------------------------- // Inline functions //----------------------------------------------------------------------------- inline void Reactor::deactivate (void) { m_active = false; } } // end namespace ASSA #endif /* REACTOR_H */