The FdEvent class registers a file descriptor for read or write events.
- The type of event is specified using an event mask. Possible events are
+ There are a number of different event types supported for file descriptors. Those are
+ specified using a bit mask. Possible events are
\li \c EV_READ: File descriptor is readable (or at EOF)
\li \c EV_PRIO: There is out-of-band data available to be read on the file descriptor
\li \c EV_WRITE: File descriptor is writable
- These event flags can be or-ed together to form an event mask. The callback will be called
- with those flags set which are currently signaled. There are additional flags which may be
- set when calling the callback:
-
+ The callback will be called with one additional argument. This argument is the event mask of
+ type int. This mask will tell, which of the registered events are signaled. There are some
+ additional flags which can be set when calling the handler callback:
+
\li \c EV_HUP: The other end has closed the connection
\li \c EV_ERR: Transport error
+ Only a single handler may be registered for any combination of file descriptor and event
+ otherwise a DuplicateEventRegistrationException is thrown.
+
The file descriptor is specified using an arbitrary handle type which supports the \c
retrieve_filehandle() protocol: There must be a global function \c retrieve_filehandle
callable with the handle type. This function must return the file descriptor associated with
typedef boost::function<void (int)> Callback;
enum Events {
- EV_NONE = 0,
- EV_READ = detail::FdManager::EV_READ,
- EV_PRIO = detail::FdManager::EV_PRIO,
- EV_WRITE = detail::FdManager::EV_WRITE,
- EV_HUP = detail::FdManager::EV_HUP,
- EV_ERR = detail::FdManager::EV_ERR,
- EV_ALL = (detail::FdManager::EV_READ
+ EV_NONE = 0 ///< No event
+ , EV_READ = detail::FdManager::EV_READ ///< fd readable (or EOF)
+ , EV_PRIO = detail::FdManager::EV_PRIO, ///< OOB data available for read
+ , EV_WRITE = detail::FdManager::EV_WRITE ///< fd writable
+ , EV_HUP = detail::FdManager::EV_HUP ///< remote end closed connection
+ , EV_ERR = detail::FdManager::EV_ERR ///< transport error
+ , EV_ALL = (detail::FdManager::EV_READ
| detail::FdManager::EV_WRITE
- | detail::FdManager::EV_PRIO)
+ | detail::FdManager::EV_PRIO) ///< register all events
};
///////////////////////////////////////////////////////////////////////////
\section scheduler_scheduler The Scheduler
- The main interface is the senf::Scheduler class. This provides support for several types of
- events:
+ The Scheduler is based on the RAII principle: Every event is represented by a class
+ instance. The event is registered in the constructor and removed by the destructor of that
+ instance. This implementation automatically links the lifetime of an event with the lifetime of
+ the object resposible for it's creation.
+
+ The Scheduler supports the following types of events::
+
\li File descriptors
\li Timers
\li UNIX signals
- \see senf::Scheduler
+ \see \ref senf::scheduler
\section scheduler_clockservice The ClockService
is met (e.g. number of chars read or a specific character sequence is found in the input).
\li senf::WriteHelper writes data to an arbitrary file descriptor until all provided data has
been written.
- \li senf::SchedulerBinding is an RAII class which manages the registration of a file descriptor
- with the %scheduler.
- \li senf::SchedulerTimer is an RAII class which manages a timer registration.
\section scheduler_i Implementation
\li Every registered event is represented by an event specific event class instance.
- \li The Dispatcher ultimately registeres with the senf::scheduler::FdManager. Since the
+ \li The Dispatcher ultimately registeres with the senf::scheduler::detail::FdManager. Since the
event-loop is based on epoll() (it could easily be changed to be based on select() or
poll()), all events must ultimately be represented by some type of file descriptor (not
necessarily a \e different file descriptor for each event).
\li The Dispatcher registeres all callbacks as tasks with the runner
- (senf::scheduler::FIFORunner).
+ (senf::scheduler::detail::FIFORunner).
- \li The senf::scheduler::FdManager uses senf::scheduler::detail::Poller to access the low-level epoll()
- API.
+ \li The senf::scheduler::detail::FdManager uses senf::scheduler::detail::Poller to access the
+ low-level epoll() API.
- All these classes are \e not singletons. They are all instantiatied by the senf::Scheduler
- singleton.
+ All these classes are singletons.
\section scheduler_i_dispatchers Dispatchers
- There is one dispatcher for each event type
+ There is a dispatcher for each event type
- \li senf::scheduler::FdDispatcher manages poll-able file descriptors. This does \e not include
- real files.
- \li senf::scheduler::FileDispatcher manages disk files
- \li senf::scheduler::TimerDispatcher manages timers
- \li senf::scheduler::SignalDispatcher manages UNIX signals
+ \li senf::scheduler::detail::FdDispatcher manages poll-able file descriptors. This does \e not
+ include real files.
+ \li senf::scheduler::detail::FileDispatcher manages disk files
+ \li senf::scheduler::detail::TimerDispatcher manages timers
+ \li senf::scheduler::detail::SignalDispatcher manages UNIX signals
- At the moment, each dispatcher has a specific API and the integration into the main-loop is not
- standardized for performance reasons.
+ Each dispatcher has a specific API and the integration into the main-loop is not standardized
+ for performance reasons.
- The Dispatcher owns all relevant data, the other classese (senf::scheduler::FIFORunner,
- senf::scheduler::FdManager) only hold pointers to the data owned by the dispatcher.
+ The Dispatcher does not own the event instances, instead those instances are owned by the
+ respective object creating the event. The implementation uses boost::intrusive containeres to
+ manage the events. This makes the Scheduler itself be completely devoid of dynamic memory
+ allocations.
\section scheduler_i_mainloop The main loop
- The application mainloop is part of senf::Scheduler. It is constructed by calling the correct
- members of all these classes repeatedly in the correct order:
+ The application mainloop senf::scheduler::process() is constructed by calling the correct
+ members of all these classes repeatedly in the correct order:
+
\li First dispatchers are set up
\li then the senf::scheduler::FdManager is called to wait for an event
\li After cleaning up the dispatchers,
namespace senf {
-/** \brief Visible scheduler interface
+/** \brief The Scheduler interface
- The %scheduler singleton manages access to the %scheduler library. It provides access to
- several event dispatchers:
- \li File descriptor notifications
- \li Timeouts
- \li UNIX Signals
+ The %scheduler API is comprised of two parts:
- The %scheduler is entered by calling it's process() member. This call will continue to run as
- long as there is something to do, or until one of the handlers calls terminate(). The
- %scheduler has 'something to do' as long as there is any file descriptor or timeout active.
+ \li Specific event classes, one for each type of event.
+ \li Some generic functions implemented in the \ref senf::scheduler namespace.
- The %scheduler only provides low level primitive scheduling capability. Additional helpers
- are defined on top of this functionality (e.g. ReadHelper or WriteHelper or the interval
- timers of the PPI).
+ Events are registered via the respective event class. The (global) functions are used to enter
+ the application main-loop or query for global information.
\section sched_handlers Specifying handlers
- All handlers are passed as generic <a
- href="http://www.boost.org/doc/html/function.html">Boost.Function</a> objects. This allows
- to pass any callable as a handler. Depending on the type of handler, some additional
- arguments may be passed to the handler by the %scheduler.
+ All handlers are specified as generic <a
+ href="http://www.boost.org/doc/html/function.html">Boost.Function</a> objects. This allows to
+ pass any callable as a handler. Depending on the type of handler, some additional arguments may
+ be passed to the handler by the %scheduler.
If you need to pass additional information to your handler, use <a
href="http://www.boost.org/libs/bind/bind.html">Boost.Bind</a>:
// Handle callback function
void callback(UDPv4ClientSocketHandle handle, senf::Scheduler::EventId event) {..}
// Pass 'handle' as additional first argument to callback()
- Scheduler::instance().add(handle, boost::bind(&callback, handle, _1), EV_READ)
+ senf::scheduler::FdEvent event ("name", boost::bind(&callback, handle, _1),
+ handle, senf::scheduler::FdEvent::EV_READ);
// Timeout function
void timeout( int n) {..}
// Call timeout() handler with argument 'n'
- Scheduler::instance().timeout(boost::bind(&timeout, n))
+ senf::scheduler::TimerEvent timer ("name", boost::bind(&timeout, n),
+ senf::ClockService::now() + senf::ClockService::seconds(1));
\endcode
To use member-functions as callbacks, use either <a
href="http://www.boost.org/libs/bind/bind.html">Boost.Bind</a> or senf::membind()
\code
// e.g. in Foo::Foo() constructor:
- Scheduler::instance().add(handle_, senf::membind(&Foo::callback, this)), EV_READ)
+ Foo::Foo()
+ : handle_ (...),
+ readevent_ ("Foo read", senf::membind(&Foo::callback, this),
+ handle_, senf::scheduler::FdEvent::EV_READ)
+ { ... }
\endcode
- The handler can also be identified by an arbitrary, user specified name. This name is used
- in error messages to identify the failing handler.
+ The handler is identified by an arbitrary, user specified name. This name is used in error
+ messages to identify the failing handler.
- \section sched_fd Registering file descriptors
-
- File descriptors are managed using add() or remove()
- \code
- Scheduler::instance().add(handle, &callback, EV_ALL);
- Scheduler::instance().remove(handle);
- \endcode
-
- The callback will be called with one additional argument. This argument is the event mask of
- type EventId. This mask will tell, which of the registered events are signaled. The
- additional flags EV_HUP or EV_ERR (on hangup or error condition) may be set additionally.
-
- Only a single handler may be registered for any combination of file descriptor and event
- (registering multiple callbacks for a single fd and event does not make sense).
-
- The %scheduler will accept any object as \a handle argument as long as retrieve_filehandle()
- may be called on that object
- \code
- int fd = retrieve_filehandle(handle);
- \endcode
- to fetch the file handle given some abstract handle type. retrieve_filehandle() will be
- found using ADL depending on the argument namespace. A default implementation is provided
- for \c int arguments (file descriptors)
+ \section sched_fd Registering events
+ Events are registered by allocating an instance of the corresponding event class:
- \section sched_timers Registering timers
-
- The %scheduler has very simple timer support. There is only one type of timer: A single-shot
- deadline timer. More complex timers are built based on this. Timers are managed using
- timeout() and cancelTimeout()
- \code
- int id = Scheduler::instance().timeout(Scheduler::instance().eventTime() + ClockService::milliseconds(100),
- &callback);
- Scheduler::instance().cancelTimeout(id);
- \endcode
- Timing is based on the ClockService, which provides a high resolution and strictly
- monotonous time source which again is based on POSIX timers. Registering a timeout will fire
- the callback when the target time is reached. The timer may be canceled by passing the
- returned \a id to cancelTimeout().
+ \li senf::scheduler::FdEvent for file descriptor events
+ \li senf::scheduler::TimerEvent for single-shot deadline timer events
+ \li senf::scheduler::SignalEvent for UNIX signal events
-
- \section sched_signals Registering POSIX/UNIX signals
-
- The %scheduler also incorporates standard POSIX/UNIX signals. Signals registered with the
- %scheduler will be handled \e synchronously within the event loop.
- \code
- Scheduler::instance().registerSignal(SIGUSR1, &callback);
- Scheduler::instance().unregisterSignal(SIGUSR1);
- \endcode
- When registering a signal with the %scheduler, that signal will automatically be blocked so
- it can be handled within the %scheduler.
-
- A registered signal does \e not count as 'something to do'. It is therefore not possible to
- wait for signals \e only.
-
- \todo Change the Scheduler API to use RAII. Additionally, this will remove all dynamic
- memory allocations from the scheduler.
+ The destructor of each of these classes will ensure, that the event will be properly
+ unregistered. The registration can be enabled, disabled or changed using appropriate
+ members. See the event class for details on a specific type of event.
+
\todo Fix the file support to use threads (?) fork (?) and a pipe so it works reliably even
over e.g. NFS.
*/