\brief Scheduler public header
*/
-#ifndef HH_Scheduler_
-#define HH_Scheduler_ 1
+#ifndef HH_SENF_Scheduler_Scheduler_
+#define HH_SENF_Scheduler_Scheduler_ 1
// Custom includes
+#include <boost/utility.hpp>
#include "../Utils/Logger/SenfLog.hh"
#include "FdEvent.hh"
#include "TimerEvent.hh"
href="http://www.boost.org/doc/libs/1_36_0/libs/ptr_container/doc/ptr_container.html">Boost.PointerContainer</a>
for the pointer container library reference.
+
+ \section sched_signals Signals and the Watchdog
+
+ To secure against blocking callbacks, the %scheduler implementation includes a watchdog
+ timer. This timer will produce a warning message on the standard error stream when a single
+ callback is executing for more than the watchdog timeout value. Since the scheduler
+ implementation is completely single threaded, we cannot terminate the callback but at least we
+ can produce an informative message and optionally the program can be aborted.
+
+ The watchdog is controlled using the watchdogTimeout(), watchdogEvents() and watchdogAbort().
+ functions.
+
+ The watchdog is implemented using a free running interval timer. The watchdog signal (\c SIGURG)
+ must \e not be blocked. If signals need to be blocked for some reason, those regions will not be
+ checked by the watchdog. If a callback blocks, the watchdog has no chance to interrupt the
+ process.
+
+ \warning Since the watchdog is free running for performance reasons, every callback must expect
+ signals to happen. Signals \e will certainly happen since the watchdog signal is generated
+ periodically (which does not necessarily generate a watchdog event ...)
+
+ Additional signals (\c SIGALRM) may occur when using using hires timers on kernel/glibc
+ combinations which do not support timerfd(). On such systems, hires timers are implemented using
+ POSIX timers which generate a considerable number of additional signals.
+
\todo Fix the file support to use threads (?) fork (?) and a pipe so it works reliably even
over e.g. NFS.
*/
\li a callback calls terminate()
\li the run queue becomes empty.
*/
- void process();
+ void process();
+
+ /** \brief \c true, if scheduler is running, \c false otherwise */
+ bool running();
/** \brief Called by callbacks to terminate the main loop
*/
ClockService::clock_type eventTime();
- /** \brief Set task watchdog timeout */
- void taskTimeout(unsigned ms);
+ /** \brief Return (approximate) current time
+
+ This call will return the current time as far as it is already known to the scheduler. If
+ the scheduler is running, this will return eventTime(), otherwise it will return
+ ClockService::now(). While the scheduler is running, this will reduce the number of system
+ calls.
+ */
+ ClockService::clock_type now();
+
+ /** \brief Set watchdog timeout to \a ms milliseconds.
+
+ Setting the watchdog timeout to 0 will disable the watchdog.
+ */
+ void watchdogTimeout(unsigned ms);
+
+ /** \brief Current watchdog timeout in milliseconds */
+ unsigned watchdogTimeout();
+
+ /** \brief Number of watchdog events
+
+ calling watchtogEvents() will reset the counter to 0
+ */
+ unsigned watchdogEvents();
+
+ /** \brief Enable/disable abort on watchdog event.
+
+ Calling watchdogAbort(\c true) will enable aborting the program execution on a watchdog
+ event.
+ */
+ void watchdogAbort(bool flag);
+
+ /** \brief Get current watchdog abort on event status */
+ bool watchdogAbort();
+
+ /** \brief Switch to using hi resolution timers
+
+ By default, timers are implemented directly using epoll. This however restricts the timer
+ resolution to that of the kernel HZ value.
+
+ High resolution timers are implemented either using POSIX timers or, when available, using
+ the Linux special \c timerfd() syscall.
+
+ POSIX timers are delivered using signals. A high timer load this increases the signal load
+ considerably. \c timerfd()'s are delivered on a file descriptor and thus don't have such a
+ scalability issue.
+
+ \warning The timer source must not be switched from a scheduler callback
+ */
+ void hiresTimers();
+
+ /** \brief Switch back to using epoll for timing
+ \see hiresTimers()
+ */
+ void loresTimers();
- /** \brief Current task watchdog timeout */
- unsigned taskTimeout();
+ /** \brief return \c true, if \c timerfd() timing is available, \c false otherwise
+ \see hiresTimers()
+ */
+ bool haveScalableHiresTimers();
- /** \brief Number of watchdog events */
- unsigned hangCount();
+ /** \brief Return \c true, if using hires times, \c false otherwise
+ \see hiresTimers() */
+ bool usingHiresTimers();
/** \brief Restart scheduler
*/
void restart();
- /** \brief Return \c true, if any event is registered, \c false otherwise. */
+ /** \brief Return \c true, if no event is registered, \c false otherwise. */
bool empty();
/** \brief %scheduler specific time source for Utils/Logger framework
senf::log::time_type operator()() const;
};
+ /** \brief Temporarily block all signals
+
+ This class is used to temporarily block all signals in a critical section.
+
+ \code
+ // Begin critical section
+ {
+ senf::scheduler::BlockSignals signalBlocker;
+
+ // critical code executed with all signals blocked
+ }
+ // End critical section
+ \endcode
+
+ You need to take care not to block since even the watchdog timer will be disabled while
+ executing within a critical section.
+ */
+ class BlockSignals
+ : boost::noncopyable
+ {
+ public:
+ BlockSignals(bool initiallyBlocked=true);
+ ///< Block signals until end of scope
+ /**< \param[in] initiallyBlocked set to \c false to not
+ automatically block signals initially */
+ ~BlockSignals(); ///< Release all signal blocks
+
+ void block(); ///< Block signals if not blocked
+ void unblock(); ///< Unblock signals if blocked
+ bool blocked() const; ///< \c true, if signals currently blocked, \c false
+ ///< otherwise
+
+ private:
+ bool blocked_;
+ sigset_t allSigs_;
+ sigset_t savedSigs_;
+ };
+
}}
///////////////////////////////hh.e////////////////////////////////////////