boost::replace_all(data, "\xff", "\xff\xff");
}
-prefix_ void senf::console::detail::ReadlineClientReader::charEvent(Scheduler::EventId event)
+prefix_ void senf::console::detail::ReadlineClientReader::charEvent(int event)
{
char ch;
if (event != Scheduler::EV_READ || handle().read(&ch, &ch+1) <= &ch) {
virtual void v_enablePrompt();
virtual void v_translate(std::string & data);
- void charEvent(Scheduler::EventId event);
+ void charEvent(int event);
static ReadlineClientReader * instance_;
int ch_;
Scheduler::instance().remove(handle_);
}
-prefix_ void senf::console::Server::newClient(Scheduler::EventId event)
+prefix_ void senf::console::Server::newClient(int event)
{
ServerHandle::ClientSocketHandle client (handle_.accept());
boost::intrusive_ptr<Client> p (new Client(*this, client));
{}
prefix_ void
-senf::console::detail::NoninteractiveClientReader::newData(senf::Scheduler::EventId event)
+senf::console::detail::NoninteractiveClientReader::newData(int event)
{
if (event != senf::Scheduler::EV_READ || handle().eof()) {
if (! buffer_.empty())
instance().servers_.erase(instance().servers_.find(server));
}
+prefix_ senf::console::detail::ServerManager & senf::console::detail::ServerManager::instance()
+{
+ static ServerManager manager;
+ return manager;
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::console::detail::NonblockingSocketSink
static Server & start(ServerHandle handle);
- void newClient(Scheduler::EventId event);
+ void newClient(int event);
void removeClient(Client & client);
ServerHandle handle_;
namespace detail {
class ServerManager
- : public senf::singleton<ServerManager>
{
public:
typedef boost::intrusive_ptr<Server> ptr;
static void add(ptr server);
static void remove(ptr server);
+ static ServerManager & instance();
+
typedef std::set<ptr> Servers;
Servers servers_;
virtual void v_enablePrompt();
virtual void v_translate(std::string & data);
- void newData(senf::Scheduler::EventId event);
+ void newData(int event);
SchedulerBinding binding_;
std::string buffer_;
}
private:
- void dumpPacket(senf::Scheduler::EventId event)
+ void dumpPacket(int event)
{
std::string data (sock.read());
senf::EthernetPacket packet (
}
private:
- void dumpPacket(senf::Scheduler::EventId event)
+ void dumpPacket(int event)
{
senf::EthernetPacket packet (
senf::EthernetPacket::create(senf::noinit));
}
private:
- void accept(senf::Scheduler::EventId event)
+ void accept(int event)
{
senf::TCPv4ClientSocketHandle clientSock (serverSock.accept());
senf::Scheduler::instance().add(
senf::Scheduler::EV_READ);
}
- void readFromClient(senf::TCPv4ClientSocketHandle clientSock, senf::Scheduler::EventId event)
+ void readFromClient(senf::TCPv4ClientSocketHandle clientSock, int event)
{
if (!clientSock) {
senf::Scheduler::instance().remove(clientSock);
}
private:
- void readFromClient(senf::Scheduler::EventId event)
+ void readFromClient(int event)
{
std::string data (serverSock.read());
std::cout << "> " << data<<std::endl ;
/** \brief Adapter to connect passive connectors
- ActiveFeeder is an adapter module which allows to connect to passive connectors. As long as
+ ActiveFeeder is an adapter module which allows two connect to passive connectors. As long as
none of the connectors is throttled, the ActiveFeeder will forward packets from it's input
to it's output.
Scheduler::instance().remove(fd_, Scheduler::EventId(events_));
}
-prefix_ void senf::ppi::IOEvent::cb(Scheduler::EventId event)
+prefix_ void senf::ppi::IOEvent::cb(int event)
{
if ((event & ~events_) != 0) {
if (event & Err)
virtual void v_enable();
virtual void v_disable();
- void cb(Scheduler::EventId event);
+ void cb(int event);
int fd_;
unsigned events_;
// Custom includes
#include "../Scheduler/Scheduler.hh"
+#include "../Utils/Logger/SenfLog.hh"
//#include "IdleEvent.mpp"
#define prefix_
prefix_ senf::ClockService::clock_type senf::ClockService::clock_m(abstime_type time)
{
+ if (now() - baseClock_ > 1000000000ll)
+ restart_m();
boost::posix_time::time_duration delta (time - baseAbstime_);
return baseClock_ + clock_type( delta.ticks() )
* clock_type( 1000000000UL / boost::posix_time::time_duration::ticks_per_second() );
prefix_ senf::ClockService::abstime_type senf::ClockService::abstime_m(clock_type clock)
{
+ if (now() - baseClock_ > 1000000000ll)
+ restart_m();
#ifdef BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
return baseAbstime_ + boost::posix_time::nanoseconds(clock-baseClock_);
#else
namespace detail { class ClockServiceTest; }
#endif
- // Implementation note:
+ // Implementation note: The clock value is represented as a 64bit unsigned integer number of
+ // nanosecods based on the CLOCK_MONOTONIC POSIX clock.
//
- // The clock value is represented as a 64bit unsigned integer number of nanosecods elapsed since
- // the construction of the ClockService object.
- //
- // The implementation must provide two features:
- // a) It must reliably detect clock changes
- // b) In case of a clock change a reasonably accurate fallback clock value must be provided
- //
- // We do this using setitimer/getitimer. We setup an interval timer sending SIGALRM whenever
- // CheckInverval seconds have elapsed.
- //
- // On every SIGALRM signal we save the current value of gettimeofday(). If this new value is
- // substantially different from the currently saved value + CheckInterval, the clock has been
- // changed.
- //
- // Whenever the current clock value is requested using now(), the current gettimeofday() value
- // is compared with the saved value. If the difference is substantially more than CheckInterval,
- // the clock has been changed.
- //
- // This provides clock skew detection. If clock skew is detected, we need to move base_ by the
- // amount the time has been changed. To do this we need an as accurate as possible approximation
- // of the expected current time value. We need to differentiate two cases:
- //
- // a) Clock skew detected within now()
- //
- // In this case, we use getitimer() to find the time remaining in the timer. Using this value
- // and the saved gettimeofday() value we can adjust base_ accordingly.
- //
- // b) Clock skew detected in the signal handler
- //
- // In this case we use the saved gettimeofday() value + CheckInterval to adjust base_.
+ // To allow conversion between clock value and absolute time, the ClockService samples the
+ // absolute current time and the clock value when the conversion is performed. This is done at
+ // most once per second on a if-needed basis.
/** \brief Reliable high precision monotonous clock source
The ClockService provides a highly accurate monotonous clock source based on
gettimeofday(). However, it takes additional precautions to detect clock skew.
- \implementation We use a mix of static and non-static members to achieve high performance
- in the normal case (no clock skew) and still encapsulate the dependency on legacy C
- headers. Using the senf::singleton mixin ensures, that the instance is constructed
- before main even when instance() is not called.
-
- \bug There is a deadlock condition between ClockService and the streaming of Boost.DateTime
- values: Boost.DateTime seems to call tzset() whenever writing a date/time value (ugh)
- and since tzset changes basic date/time values, it seems to block gettimeofday() which
- leads to the SIGLARM handler blocking indefinitely. Resolution either a) find out, why
- tzset() of all functions is called or b) move the ClockService heartbeat functionality
- into the Scheduler.
+ \implementation The funny mixture of static and non-static members stems from the old
+ implementation based on interval timers and gettimeofday(). The current implementation
+ usses POSIX clocks and is much simpler and more precise.
*/
class ClockService
: singleton<ClockService>
nanoseconds relative to some implementation defined reference time.
*/
typedef boost::int_fast64_t clock_type;
+
+ /** \brief Supplementary integer type
+
+ This type is used to represent varies supplementary values (e.g. number of microseconds)
+ */
typedef boost::int_fast64_t int64_type;
/** \brief Absolute time data type
static int64_type in_hours(clock_type v); ///< Convert \a v to hours
static int64_type in_days(clock_type v); ///< Convert \a v to days
- static void restart();
+ static void restart(); ///< Force re-syncronisation of abstime and clock
+ /**< Calling the member should never be necessary since
+ abstime() / clock() automatically call restart() if
+ needed */
private:
ClockService();
//#include "FIFORunner.ih"
// Custom includes
+#include <signal.h>
+#include <time.h>
+#include "../Utils/Exception.hh"
//#include "FIFORunner.mpp"
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
+prefix_ senf::scheduler::FIFORunner::FIFORunner()
+ : tasks_ (), next_ (tasks_.end()), hangCount_ (0)
+{
+ struct sigevent ev;
+ ::memset(&ev, 0, sizeof(ev));
+ ev.sigev_notify = SIGEV_SIGNAL;
+ ev.sigev_signo = SIGURG;
+ ev.sigev_value.sival_ptr = this;
+ if (timer_create(CLOCK_MONOTONIC, &ev, &watchdogId_) < 0)
+ SENF_THROW_SYSTEM_EXCEPTION("timer_create()");
+
+ struct sigaction sa;
+ ::memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = &watchdog;
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGURG, &sa, 0) < 0)
+ SENF_THROW_SYSTEM_EXCEPTION("sigaction()");
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGURG);
+ if (sigprocmask(SIG_UNBLOCK, &mask, 0) < 0)
+ SENF_THROW_SYSTEM_EXCEPTION("sigprocmask()");
+}
+
+prefix_ senf::scheduler::FIFORunner::~FIFORunner()
+{
+ timer_delete(watchdogId_);
+ signal(SIGURG, SIG_DFL);
+}
+
// At the moment, the FIFORunner is not very efficient with many non-runnable tasks since the
// complete list of tasks is traversed on each run().
//
// To optimize this, we woould need a way to find the relative ordering of two tasks in O(1) (at the
-// moment, this is an O)(N) operation by traversing the list).
+// moment, this is an O(N) operation by traversing the list).
//
// One idea is, to give each task an 'order' value. Whenever a task is added at the end, it's order
// value is set to the order value of the last task + 1. Whenever the order value such added exceeds
tasks_.push_back(null);
TaskList::iterator end (TaskList::current(null));
next_ = tasks_.begin();
+ struct itimerspec timer;
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_nsec = 0;
+ timer.it_value.tv_sec = 1;
+ timer.it_value.tv_nsec = 0;
while (next_ != end) {
TaskInfo & task (*next_);
if (task.runnable) {
task.runnable = false;
+ if (timer_settime(watchdogId_, 0, &timer, 0) < 0)
+ SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
+ runningName_ = task.name;
+# ifdef SENF_DEBUG
+ runningBacktrace_ = task.backtrace;
+# endif
TaskList::iterator i (next_);
++ next_;
tasks_.splice(tasks_.end(), tasks_, i);
else
++ next_;
}
+ timer.it_value.tv_sec = 0;
+ if (timer_settime(watchdogId_, 0, &timer, 0) < 0)
+ SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
tasks_.erase(end);
next_ = tasks_.end();
}
+prefix_ void senf::scheduler::FIFORunner::watchdog(int, siginfo_t * si, void *)
+{
+ FIFORunner & runner (*static_cast<FIFORunner *>(si->si_value.sival_ptr));
+ ++ runner.hangCount_;
+ write(1, "\n\n*** Scheduler task hanging: ", 30);
+ write(1, runner.runningName_.c_str(), runner.runningName_.size());
+ write(1, "\n", 1);
+#ifdef SENF_DEBUG
+ write(1, "Task was initialized at\n", 24);
+ write(1, runner.runningBacktrace_.c_str(), runner.runningBacktrace_.size());
+#endif
+ write(1, "\n", 1);
+}
+
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
//#include "FIFORunner.mpp"
//#include "FIFORunner.ih"
// Custom includes
+#ifdef SENF_DEBUG
+#include <sstream>
+#include "../Utils/Backtrace.hh"
+#endif
#define prefix_ inline
///////////////////////////////cci.p///////////////////////////////////////
prefix_ senf::scheduler::FIFORunner::TaskInfo::~TaskInfo()
{}
-prefix_ senf::scheduler::FIFORunner::FIFORunner()
- : tasks_ (), next_ (tasks_.end())
-{}
-
prefix_ void senf::scheduler::FIFORunner::enqueue(TaskInfo * task)
{
tasks_.push_back(*task);
+#ifdef SENF_DEBUG
+ std::stringstream ss;
+ backtrace(ss, 32);
+ task->backtrace = ss.str();
+#endif
+}
+
+prefix_ unsigned senf::scheduler::FIFORunner::hangCount()
+ const
+{
+ return hangCount_;
}
///////////////////////////////cci.e///////////////////////////////////////
#define HH_FIFORunner_ 1
// Custom includes
+#include <signal.h>
#include <boost/utility.hpp>
#include "../boost/intrusive/ilist.hpp"
#include "../boost/intrusive/ilist_hook.hpp"
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Task execution scheduler
+
+ The FIFORunner implements a simple FIFO scheduler for callback tasks. All tasks are held in
+ a queue. Whenever a task is run, it is moved to the end of the queue. Running the queue will
+ run all tasks which have been marked runnable.
+
+ When running a task, it's runnable flag is always reset. The flag is set whenever an event
+ is posted for the task.
*/
class FIFORunner
: boost::noncopyable
///////////////////////////////////////////////////////////////////////////
// Types
+ /** \brief Task structure
+
+ TaskInfo is the base-class for all tasks.
+ */
struct TaskInfo
: public TaskListBase
{
TaskInfo();
virtual ~TaskInfo();
- bool runnable;
- virtual void run() = 0;
+ bool runnable; ///< Runnable flag
+ /**< This must be set to \c true when the task is
+ runnable. It is reset automatically when the task is
+ run. */
+
+ std::string name; ///< Descriptive task name
+# ifdef SENF_DEBUG
+ std::string backtrace;
+# endif
+ virtual void run() = 0; ///< Called to run the task
};
///////////////////////////////////////////////////////////////////////////
///@{
FIFORunner();
+ ~FIFORunner();
///@}
///////////////////////////////////////////////////////////////////////////
- void enqueue(TaskInfo * task);
- void dequeue(TaskInfo * task);
+ void enqueue(TaskInfo * task); ///< Add task to queue
+ void dequeue(TaskInfo * task); ///< Remove task from queue
- void run();
+ void run(); ///< Run queue
+
+ unsigned hangCount() const; ///< Number of task expirations
+ /**< The FIFORunner manages a watchdog which checks, that a
+ single task does not run continuously for a longer time
+ or block. If a task runs for more than 1s, a warning is
+ printed and the hangCount is increased. */
protected:
private:
+ static void watchdog(int, siginfo_t *, void *);
+
TaskList tasks_;
TaskList::iterator next_;
+ int watchdogId_;
+ std::string runningName_;
+# ifdef SENF_DEBUG
+ std::string runningBacktrace_;
+# endif
+ unsigned hangCount_;
};
}
}
-prefix_ void senf::scheduler::FdDispatcher::add(int fd, Callback const & cb, int events)
+prefix_ bool senf::scheduler::FdDispatcher::add(std::string const & name, int fd,
+ Callback const & cb, int events)
{
if (events == 0)
- return;
+ return true;
FdMap::iterator i (fds_.find(fd));
if (i == fds_.end()) {
}
FdEvent & event (i->second);
- if (events & EV_READ) event.FdEvent::ReadTask::cb = cb;
- if (events & EV_PRIO) event.FdEvent::PrioTask::cb = cb;
- if (events & EV_WRITE) event.FdEvent::WriteTask::cb = cb;
+ if (events & EV_READ) {
+ event.FdEvent::ReadTask::cb = cb;
+ event.FdEvent::ReadTask::name = name;
+ }
+ if (events & EV_PRIO) {
+ event.FdEvent::PrioTask::cb = cb;
+ event.FdEvent::PrioTask::name = name;
+ }
+ if (events & EV_WRITE) {
+ event.FdEvent::WriteTask::cb = cb;
+ event.FdEvent::WriteTask::name = name;
+ }
- manager_.set(fd, event.activeEvents(), &event);
+ if (! manager_.set(fd, event.activeEvents(), &event)) {
+ runner_.dequeue(static_cast<FdEvent::ReadTask*>(&i->second));
+ runner_.dequeue(static_cast<FdEvent::PrioTask*>(&i->second));
+ runner_.dequeue(static_cast<FdEvent::WriteTask*>(&i->second));
+ fds_.erase(i);
+ return false;
+ }
+ else
+ return true;
}
prefix_ void senf::scheduler::FdDispatcher::remove(int fd, int events)
return;
FdEvent & event (i->second);
- if (events & EV_READ) event.FdEvent::ReadTask::cb = 0;
- if (events & EV_PRIO) event.FdEvent::PrioTask::cb = 0;
- if (events & EV_WRITE) event.FdEvent::WriteTask::cb = 0;
+ if (events & EV_READ) {
+ event.FdEvent::ReadTask::cb = 0;
+ event.FdEvent::ReadTask::name.clear();
+ }
+ if (events & EV_PRIO) {
+ event.FdEvent::PrioTask::cb = 0;
+ event.FdEvent::PrioTask::name.clear();
+ }
+ if (events & EV_WRITE) {
+ event.FdEvent::WriteTask::cb = 0;
+ event.FdEvent::WriteTask::name.clear();
+ }
int activeEvents (event.activeEvents());
if (! activeEvents) {
if (events & EV_WRITE)
WriteTask::runnable = true;
- if (events & (EV_ERR | EV_HUP) && ! events & (EV_READ | EV_PRIO | EV_WRITE)) {
+ if ((events & (EV_ERR | EV_HUP)) && ! (events & (EV_READ | EV_PRIO | EV_WRITE))) {
if (ReadTask::cb) ReadTask::runnable = true;
if (PrioTask::cb) PrioTask::runnable = true;
if (WriteTask::cb) WriteTask::runnable = true;
: manager_ (manager), runner_ (runner)
{}
+prefix_ bool senf::scheduler::FdDispatcher::empty()
+ const
+{
+ return fds_.empty();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::scheduler::FdDispatcher::FdEvent
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Scheduler dispatcher managing poll-able file descriptors
+
+ File descriptors are added directly to the event loop.
*/
class FdDispatcher
{
///@}
///////////////////////////////////////////////////////////////////////////
- void add(int fd, Callback const & cb, int events = EV_ALL);
- void remove(int fd, int events = EV_ALL);
+ bool add(std::string const & name, int fd, Callback const & cb, int events = EV_ALL);
+ ///< Add file descriptor callback
+ /**< There is always one active callback for each
+ combination of file descriptor and event. Registering a
+ new callback will overwrite the old callback.
+ \param[in] name descriptive name
+ \param[in] fd file descriptor
+ \param[in] cb callback
+ \param[in] events Events to call \a cb for */
+
+ void remove(int fd, int events = EV_ALL); ///< Remove callback
+ /**< \param[in] fd file descriptor
+ \param[in] events Events for which to remove the
+ callback */
+
+ bool empty() const; ///< \c true, if no file descriptors are registered.
protected:
private:
+ /// Internal: File descriptor event
struct FdEvent
: public detail::FdTask<0, FdEvent>,
public detail::FdTask<1, FdEvent>,
namespace scheduler {
namespace detail {
+ /** \brief Internal: File descriptor task
+
+ \implementation This class allows to inherit FIFORunner::TaskInfo multiple times to add
+ multiple tasks to one event and still allows efficient access to the event class
+ */
template <unsigned index, class Self>
struct FdTask
: public FIFORunner::TaskInfo
BOOST_FAIL("connect");
}
- SENF_CHECK_NO_THROW( dispatcher.add(sock, boost::bind(&callback, sock, _1),
- senf::scheduler::FdDispatcher::EV_READ) );
+ BOOST_CHECK( dispatcher.add("testHandler", sock, boost::bind(&callback, sock, _1),
+ senf::scheduler::FdDispatcher::EV_READ) );
event = 0;
SENF_CHECK_NO_THROW( manager.processOnce() );
SENF_CHECK_NO_THROW( runner.run() );
strcpy(buffer,"WRITE");
size=5;
- SENF_CHECK_NO_THROW( dispatcher.add(sock, boost::bind(&callback, sock, _1),
- senf::scheduler::FdDispatcher::EV_WRITE) );
+ BOOST_CHECK( dispatcher.add("testHandler", sock, boost::bind(&callback, sock, _1),
+ senf::scheduler::FdDispatcher::EV_WRITE) );
event = 0;
sleep(1);
SENF_CHECK_NO_THROW( manager.processOnce() );
prefix_ void senf::scheduler::FdManager::processOnce()
{
Poller<Event>::range events (poller_.wait());
+ eventTime_ = ClockService::now();
for (Poller<Event>::iterator i (events.begin()); i != events.end(); ++i)
i->second->signal(i->first);
}
///////////////////////////////////////////////////////////////////////////
// senf::scheduler::FdManager
-prefix_ void senf::scheduler::FdManager::set(int fd, int events, Event * entry)
+prefix_ senf::scheduler::FdManager::FdManager()
+ : eventTime_ (senf::ClockService::now())
+{}
+
+prefix_ bool senf::scheduler::FdManager::set(int fd, int events, Event * entry)
{
- poller_.set(fd, events, entry);
+ return poller_.set(fd, events, entry);
}
prefix_ void senf::scheduler::FdManager::remove(int fd)
return poller_.timeout();
}
+prefix_ senf::ClockService::clock_type senf::scheduler::FdManager::eventTime()
+ const
+{
+ return eventTime_;
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::scheduler::FdManager::Event
// Custom includes
#include "Poller.hh"
+#include "ClockService.hh"
//#include "FdManager.mpp"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Manage file descriptor event processing
+
+ The FdManager is the internal class which manages all events (all events need to somehow be
+ made accessible via a file descriptor). File descriptors are added or removed from the
+ FdManager which then allows waiting until an event occurs on one of the descriptors.
+
+ Registered events must be derived from FdManager::Event. The FdManager does \e not manage
+ the event classes, it just manages pointers to externally owned events (the events are owned
+ by the respective dispatchers).
+
+ When an event is posted, it's \c signal() member is called. However, this call will \e not
+ execute the user callback registered for the event, it will just mark the relevant tasks as
+ runnable.
+
+ \implementation
*/
class FdManager
{
///////////////////////////////////////////////////////////////////////////
// Types
+ ///< Event baseclass
struct Event {
virtual ~Event();
- virtual void signal(int events) = 0;
+ virtual void signal(int events) = 0; ///< Called when the given event is posted
};
enum Events {
///\name Structors and default members
///@{
+ FdManager();
+
///@}
///////////////////////////////////////////////////////////////////////////
- void set(int fd, int events, Event * entry);
- void remove(int fd);
-
- void timeout(int t);
- int timeout() const;
-
- void processOnce();
+ bool set(int fd, int events, Event * entry); ///< Set file descriptor event mask
+ /**< This sets the event mask for \a fd to \a events which
+ is a combination of values from the \c Events enum. If
+ \a fd is already registered, the registration is
+ changed to conform to the parameters passed, otherwise
+ a new registration is added.
+ \param[in] fd file descriptor
+ \param[in] events events to register for
+ \param[in] entry event to signal
+ \returns \c true, if \a fd supports polling, \c false
+ otherwise */
+ void remove(int fd); ///< Remove \a fd from the manager
+
+ void timeout(int t); ///< Set event timeout
+ /**< proceseOnce() will wait for max \a t milliseconds for
+ an event to occur. If set to -1, processOnce() will
+ wait forever. */
+ int timeout() const; ///< Get timeout in milliseconds
+
+ void processOnce(); ///< Wait for events
+ /**< This call waits until at least one event is posted but
+ no longer than the current timeout(). */
+
+ ClockService::clock_type eventTime() const; ///< Time of last event
protected:
private:
Poller<Event> poller_;
+ senf::ClockService::clock_type eventTime_;
};
}}
}
}
-prefix_ void senf::scheduler::FileDispatcher::add(int fd, Callback const & cb, int events)
+prefix_ void senf::scheduler::FileDispatcher::add(std::string const & name, int fd,
+ Callback const & cb, int events)
{
if (events == 0)
return;
}
FileEvent & event (i->second);
- if (events & EV_READ) event.FileEvent::ReadTask::cb = cb;
- if (events & EV_WRITE) event.FileEvent::WriteTask::cb = cb;
+ if (events & EV_READ) {
+ event.FileEvent::ReadTask::cb = cb;
+ event.FileEvent::ReadTask::name = name;
+ }
+ if (events & EV_WRITE) {
+ event.FileEvent::WriteTask::cb = cb;
+ event.FileEvent::WriteTask::name = name;
+ }
manager_.timeout(0);
}
return managerTimeout_;
}
+prefix_ bool senf::scheduler::FileDispatcher::empty()
+ const
+{
+ return files_.empty();
+}
+
///////////////////////////////cci.e///////////////////////////////////////
#undef prefix_
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Scheduler dispatcher managing disc files
+
+ This dispatcher manages file descriptors which are connected to disc files. Since disc files
+ do not support select() / poll() / epoll(), they are considered to be always ready (which is
+ especially untrue for remote files e.g. vie NFS).
+
+ The FileDispatcher will change the FdManager's event timeout value to 0 (from -1) whenever
+ there is at least one file registered.
*/
class FileDispatcher
{
///@}
///////////////////////////////////////////////////////////////////////////
- void add(int fd, Callback const & cb, int events = EV_ALL);
+ void add(std::string const & name, int fd, Callback const & cb, int events = EV_ALL);
+ ///< Add file descriptor callback
+ /**< There is always one active callback for each
+ combination of file descriptor and event. Registering a
+ new callback will overwrite the old callback.
+ \param[in] name descriptive name
+ \param[in] fd file descriptor
+ \param[in] cb callback
+ \param[in] events Events to call \a cb for */
+
void remove(int fd, int events = EV_ALL);
+ /**< \param[in] fd file descriptor
+ \param[in] events Events for which to remove the
+ callback */
+
+ void prepareRun(); ///< Prepare tasks
+ /**< This must be called after the FdManager returns before
+ running the runnable tasks. */
- void prepareRun();
+ void timeout(int t); ///< Change FdManager timeout
+ /**< Since the FileDispatcher must be able to change the
+ timeout value, the value must be set here and not
+ directly in the FdManager. */
+ int timeout() const; ///< Retrieve current timeout value
- void timeout(int t);
- int timeout() const;
+ bool empty() const; ///< \c true, if no files are registered.
protected:
private:
+ /// Internal: Disk file event
struct FileEvent
: public detail::FdTask<0, FileEvent>,
public detail::FdTask<1, FileEvent>
int fd (open("/dev/null", O_RDWR));
senf::ClockService::clock_type t (senf::ClockService::now());
- SENF_CHECK_NO_THROW( dispatcher.add(fd, &handler, senf::scheduler::FileDispatcher::EV_READ) );
+ SENF_CHECK_NO_THROW( dispatcher.add("testHandler", fd, &handler,
+ senf::scheduler::FileDispatcher::EV_READ) );
SENF_CHECK_NO_THROW( manager.processOnce() );
SENF_CHECK_NO_THROW( dispatcher.prepareRun() );
SENF_CHECK_NO_THROW( runner.run() );
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-namespace senf {
-
/** \mainpage The SENF Scheduler Library
- The Scheduler library provides a simple yet flexible abstraction of the standard asynchronous
- UNIX mainloop utilizing \c select or \c poll. The Scheduler library is based on the highly
- efficient (but linux specific) \c epoll() system call.
+ The %Scheduler Library provides a single-threaded application event-loop multiplexing multiple
+ event sources.
+
+ \autotoc
- The library provides
- \li the ClockService as a reliable high-resolution highly accurate monotonous time source
- \li a central \ref Scheduler %singleton and
- \li \ref ReadHelper and \ref WriteHelper templates to simplify common tasks.
+ \section scheduler_scheduler The Scheduler
- The Scheduler supports several types of scheduling activites:
- \li Arbitrary file descriptors (however, local disk file-handles are not guaranteed
- non-blocking)
+ The main interface is the senf::Scheduler class. This provides support for several types of
+ events:
+ \li File descriptors
\li Timers
- \li UNIX Signals
+ \li UNIX signals
+
+ \see senf::Scheduler
+
+
+ \section scheduler_clockservice The ClockService
+
+ To support precise event timing, the senf::ClockService class implements a reliable monotonous
+ time source. It is based on the high precision POSIX clock and adds support for reliable
+ conversion between an abstract clock type and absolute date/time
+
+ \see senf::ClockService
+
+
+ \section scheduler_helpers Miscellaneous helpers
+
+ To ease the use of the Scheduler there are some additional helpers managing callbacks and
+ registrations.
+
+ \li senf::ReadHelper reads data from an arbitrary file descritor until a use specified condition
+ 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
+
+ senf::Scheduler is only a wrapper around the real implementation. The real implementation is now
+ based on a modular dispatcher architecture
+
+ \see \ref scheduler_implementation
*/
-}
+/** \page scheduler_implementation The Scheduler Implementation
+
+ The implentation architecture now is based on a set of dispatchers, one for each type of
+ event.
+
+ \autotoc
+
+ \section scheduler_i_overview Overview
+
+ The %scheduler utilizes the following components
+
+ \li There is a dispatcher for each event type. This dispatcher manages the event specific
+ registration and unregistration. The dispatcher is owns the event (and task) objects.
+
+ \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
+ 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).
+
+ \li The senf::scheduler::FdManager uses senf::scheduler::Poller to access the low-level epoll()
+ API.
+
+ All these classes are \e not singletons. They are all instantiatied by the senf::Scheduler
+ singleton.
+
+
+ \section scheduler_i_dispatchers Dispatchers
+
+ There is one 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
+
+ At the moment, 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.
+
+
+ \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:
+ \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,
+ \li the senf::scheduler::FIFORunner is called to executed all now runnable tasks.
+ */
\f
// Local Variables:
// ispell-local-dictionary: "american"
// mode: flyspell
// mode: auto-fill
+// compile-command: "scons -U doc"
// End:
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Epoll abstraction
+
+ This class provides a more convenient interface to the epoll() API. File descriptors are
+ registered with pointers to a parameterized event type. After waiting for an event, the
+ Poller allows to iterate over the event instances for all posted events.
+
+ \tparam Value Event type
*/
template <class Value>
class Poller
///@}
///////////////////////////////////////////////////////////////////////////
- bool set(int fd, int events, Value * data);
- void remove(int fd);
- range wait();
+ bool set(int fd, int events, Value * data); ///< Set file descriptor event data and mask
+ /**< The Poller does \e not own \a data. The value is owned
+ by some external entity (the dispatcher to be more
+ precise). */
+ void remove(int fd); ///< Remove file descriptor
+ range wait(); ///< Wait for one event
+ /**< \returns a range of iterators which iterate over the
+ data values registered with the event */
- void timeout(int t);
- int timeout() const;
+ void timeout(int t); ///< Set event timeout to \a t milliseconds
+ int timeout() const; ///< Current event timeout
private:
int epollFd_;
template <class Handle>
prefix_ void
-senf::ReadHelper<Handle>::dispatchProcess(ptr helper, Handle handle,
- senf::Scheduler::EventId event)
+senf::ReadHelper<Handle>::dispatchProcess(ptr helper, Handle handle, int event)
{
// since we have a 'ptr' argument, the instance cannot be deleted
// before this method returns
}
template <class Handle>
-prefix_ void senf::ReadHelper<Handle>::process(Handle handle,
- senf::Scheduler::EventId event)
+prefix_ void senf::ReadHelper<Handle>::process(Handle handle,int event)
{
try {
if (event != senf::Scheduler::EV_READ)
ReadHelper(Handle handle, unsigned maxSize, InternalPredicate * predicate, Callback cb);
- static void dispatchProcess(ptr helper, Handle handle, senf::Scheduler::EventId event);
- void process(Handle handle, senf::Scheduler::EventId event);
+ static void dispatchProcess(ptr helper, Handle handle, int event);
+ void process(Handle handle, int event);
void done();
Handle handle_;
threads)
*/
-// Here a basic concept of how to add signal support to the scheduler:
-//
-// ... no, I had overlooked one race condition. So back to the signal-pipe approach ...
-
#include "Scheduler.hh"
//#include "Scheduler.ih"
// Custom includes
-#include "../Utils/senfassert.hh"
-#include <errno.h>
-#include <sys/epoll.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include "../Utils/Exception.hh"
-#include "../Utils/Backtrace.hh"
-
-static const int EPollInitialSize = 16;
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
-prefix_ senf::Scheduler::Scheduler()
- : files_(0), timerIdCounter_(0), epollFd_ (epoll_create(EPollInitialSize)), terminate_(false),
- eventTime_(0), eventEarly_(ClockService::milliseconds(11)), eventAdjust_(0)
-{
- if (epollFd_<0)
- SENF_THROW_SYSTEM_EXCEPTION("::epoll_create()");
-
- if (::pipe(sigpipe_) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::pipe()");
-
- int flags (::fcntl(sigpipe_[1],F_GETFL));
- if (flags < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::fcntl(F_GETFL)");
- flags |= O_NONBLOCK;
- if (::fcntl(sigpipe_[1], F_SETFL, flags) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::fcntl(F_SETFL)");
-
- ::epoll_event ev;
- ::memset(&ev, 0, sizeof(ev));
- ev.events = EV_READ;
- ev.data.fd = sigpipe_[0];
- if (::epoll_ctl(epollFd_, EPOLL_CTL_ADD, sigpipe_[0], &ev) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::epoll_ctl(EPOLL_CTL_ADD)");
-}
-
-prefix_ void senf::Scheduler::registerSignal(unsigned signal, SimpleCallback const & cb)
-{
- ::sigset_t sig;
- ::sigemptyset(&sig);
- if (::sigaddset(&sig, signal) < 0)
- throw InvalidSignalNumberException();
- ::sigprocmask(SIG_BLOCK, &sig, 0);
- ::sigaddset(&sigset_, signal);
- if (sigHandlers_.size() <= signal)
- sigHandlers_.resize(signal+1);
- sigHandlers_[signal] = cb;
-
- registerSigHandlers();
-}
-
-prefix_ void senf::Scheduler::unregisterSignal(unsigned signal)
-{
- if (::sigdelset(&sigset_, signal) < 0)
- throw InvalidSignalNumberException();
- sigHandlers_[signal] = 0;
- ::signal(signal, SIG_DFL);
- registerSigHandlers();
-}
-
-prefix_ void senf::Scheduler::do_add(int fd, FdCallback const & cb, int eventMask)
-{
- if (eventMask == 0)
- return;
-
- FdTable::iterator i (fdTable_.find(fd));
- int action (EPOLL_CTL_MOD);
- if (i == fdTable_.end()) {
- action = EPOLL_CTL_ADD;
- i = fdTable_.insert(std::make_pair(fd, EventSpec())).first;
- }
- else if (i->second.epollMask() == 0) {
- action = EPOLL_CTL_ADD;
- fdErase_.erase( std::remove(fdErase_.begin(), fdErase_.end(), unsigned(fd)),
- fdErase_.end() );
- }
-
- if (eventMask & EV_READ) i->second.cb_read = cb;
- if (eventMask & EV_PRIO) i->second.cb_prio = cb;
- if (eventMask & EV_WRITE) i->second.cb_write = cb;
-
- epoll_event ev;
- memset(&ev,0,sizeof(ev));
- ev.events = i->second.epollMask();
- ev.data.fd = fd;
-
- for (;;) {
- if ( (!i->second.file) && (epoll_ctl(epollFd_, action, fd, &ev) < 0) ) {
- switch (errno) {
- case EPERM :
- // Argh ... epoll does not support ordinary files :-( :-(
- i->second.file = true;
- ++ files_;
- return;
- case ENOENT :
- action = EPOLL_CTL_ADD;
- break;
- default:
- SENF_THROW_SYSTEM_EXCEPTION("::epoll_ctl()");
- }
- }
- else
- return;
- }
-}
-
-prefix_ void senf::Scheduler::do_remove(int fd, int eventMask)
-{
- if (eventMask == 0)
- return;
-
- FdTable::iterator i (fdTable_.find(fd));
- if (i == fdTable_.end())
- return;
-
- if (eventMask & EV_READ) i->second.cb_read = 0;
- if (eventMask & EV_PRIO) i->second.cb_prio = 0;
- if (eventMask & EV_WRITE) i->second.cb_write = 0;
-
- epoll_event ev;
- memset(&ev,0,sizeof(ev));
- ev.events = i->second.epollMask();
- ev.data.fd = fd;
-
- int action (EPOLL_CTL_MOD);
- bool file (i->second.file);
- if (ev.events==0) {
- action = EPOLL_CTL_DEL;
- fdErase_.push_back(fd);
- }
-
- if (! file && epoll_ctl(epollFd_, action, fd, &ev) < 0 && errno != ENOENT)
- SENF_THROW_SYSTEM_EXCEPTION("::epoll_ctl()");
- if (file)
- -- files_;
-}
-
-prefix_ void senf::Scheduler::registerSigHandlers()
-{
- for (unsigned signal (1); signal < sigHandlers_.size(); ++signal) {
- if (sigHandlers_[signal]) {
- struct ::sigaction sa;
- sa.sa_sigaction = & Scheduler::sigHandler;
- sa.sa_mask = sigset_;
- sa.sa_flags = SA_SIGINFO;
- if (signal == SIGCHLD)
- sa.sa_flags |= SA_NOCLDSTOP;
- if (::sigaction(signal, &sa, 0) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::sigaction()");
- }
- }
-}
-
-prefix_ void senf::Scheduler::sigHandler(int signal, ::siginfo_t * siginfo, void *)
-{
- // This is a bit unsafe. Better write single bytes and place the siginfo into an explicit
- // queue. Since signals are only unblocked during epoll_wait, we even wouldn't need to
- // synchronize access to that queue any further.
-
- ::write(instance().sigpipe_[1], siginfo, sizeof(*siginfo));
-
- // We ignore errors. The file handle is set to non-blocking IO. If any failure occurs (pipe
- // full), the signal will be dropped. That's like kernel signal handling which may also drop
- // signals.
-}
-
-prefix_ int senf::Scheduler::EventSpec::epollMask()
- const
-{
- int mask (0);
- if (cb_read) mask |= EPOLLIN;
- if (cb_prio) mask |= EPOLLPRI;
- if (cb_write) mask |= EPOLLOUT;
- return mask;
-}
-
prefix_ void senf::Scheduler::process()
{
terminate_ = false;
- eventTime_ = ClockService::now();
- while (! terminate_) {
-
- // Since a callback may have disabled further timers, we need to check for canceled timeouts
- // again.
-
- while (! timerQueue_.empty()) {
- TimerMap::iterator i (timerQueue_.top());
- if (! i->second.canceled)
- break;
- timerMap_.erase(i);
- timerQueue_.pop();
- }
-
- for (FdEraseList::iterator i (fdErase_.begin()); i != fdErase_.end(); ++i)
- fdTable_.erase(*i);
- fdErase_.clear();
-
- int timeout (-1);
- if (files_ > 0)
- timeout = 0;
- else {
- if (timerQueue_.empty()) {
- if (fdTable_.empty())
- break;
- }
- else {
- ClockService::clock_type delta (
- (timerQueue_.top()->second.timeout - eventTime_ + eventAdjust_)/1000000UL);
- timeout = delta < 0 ? 0 : delta;
- }
- }
-
- ///\todo Handle more than one epoll_event per call
- struct epoll_event ev;
-
- if (::sigprocmask(SIG_UNBLOCK, &sigset_, 0) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::sigprocmask(SIG_UNBLOCK)");
- int events (::epoll_wait(epollFd_, &ev, 1, timeout));
- if (::sigprocmask(SIG_BLOCK, &sigset_, 0) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::sigprocmask(SIG_BLOCK)");
-
- if (events<0)
- if (errno != EINTR)
- SENF_THROW_SYSTEM_EXCEPTION("::epoll_wait()");
-
- eventTime_ = ClockService::now();
-
- // We always run timeout handlers. This is important, even if a file-descriptor is signaled
- // since some descriptors (e.g. real files) will *always* be ready and we still may want to
- // handle timers. Time handlers are run before file events to not delay them unnecessarily.
-
- while (! timerQueue_.empty()) {
- TimerMap::iterator i (timerQueue_.top());
- if (i->second.canceled)
- ;
- else if (i->second.timeout <= eventTime_ + eventEarly_)
- i->second.cb();
- else
- break;
- timerQueue_.pop();
- timerMap_.erase(i);
- }
-
- // Check the signal queue
- if (events > 0 && ev.data.fd == sigpipe_[0]) {
- ::siginfo_t siginfo;
- if (::read(sigpipe_[0], &siginfo, sizeof(siginfo)) < int(sizeof(siginfo))) {
- // We ignore truncated records which may only occur if the signal
- // queue became filled up
- SENF_LOG((senf::log::IMPORTANT)("Truncated signal record!"));
- continue;
- }
- if (siginfo.si_signo < int(sigHandlers_.size()) && sigHandlers_[siginfo.si_signo])
- sigHandlers_[siginfo.si_signo]();
- continue;
- }
-
- for (FdTable::iterator i = fdTable_.begin(); i != fdTable_.end(); ++i) {
- EventSpec & spec (i->second);
-
- if (! (spec.file || (events > 0 && i->first == ev.data.fd)))
- continue;
-
- unsigned extraFlags (0);
- unsigned mask (spec.file ? spec.epollMask() : ev.events);
-
- if (mask & EPOLLHUP) extraFlags |= EV_HUP;
- if (mask & EPOLLERR) extraFlags |= EV_ERR;
-
- if (mask & EPOLLIN) {
- SENF_ASSERT(spec.cb_read);
- spec.cb_read(EventId(EV_READ | extraFlags));
- }
- else if (mask & EPOLLPRI) {
- SENF_ASSERT(spec.cb_prio);
- spec.cb_prio(EventId(EV_PRIO | extraFlags));
- }
- else if (mask & EPOLLOUT) {
- SENF_ASSERT(spec.cb_write);
- spec.cb_write(EventId(EV_WRITE | extraFlags));
- }
- else {
- // This branch is only taken, if HUP or ERR is signaled but none of IN/OUT/PRI.
- // In this case we will signal all registered callbacks. The callbacks must be
- // prepared to be called multiple times if they are registered to more than
- // one event.
- if (spec.cb_write)
- spec.cb_write(EventId(extraFlags));
- if (spec.cb_prio)
- spec.cb_prio(EventId(extraFlags));
- if (spec.cb_read)
- spec.cb_read(EventId(extraFlags));
- }
- }
+ while(! terminate_ && ! (fdDispatcher_.empty() &&
+ timerDispatcher_.empty() &&
+ fileDispatcher_.empty())) {
+ signalDispatcher_.unblockSignals();
+ timerDispatcher_.unblockSignals();
+ manager_.processOnce();
+ timerDispatcher_.blockSignals();
+ signalDispatcher_.blockSignals();
+ fileDispatcher_.prepareRun();
+ runner_.run();
}
}
//#include "Scheduler.ih"
// Custom includes
+#include <boost/format.hpp>
#define prefix_ inline
///////////////////////////////cci.p///////////////////////////////////////
+// private members
+
+prefix_ void senf::Scheduler::do_add(int fd, FdCallback const & cb, int eventMask)
+{
+ do_add((boost::format("<anon fd %d>") % fd).str(), fd, cb, eventMask);
+}
+
+prefix_ void senf::Scheduler::do_add(std::string const & name, int fd, FdCallback const & cb,
+ int eventMask)
+{
+ if (! fdDispatcher_.add(name, fd, cb, eventMask))
+ fileDispatcher_.add(name, fd, cb, eventMask);
+}
+
+prefix_ void senf::Scheduler::do_remove(int fd, int eventMask)
+{
+ // We don't know, where the descriptor is registered. However, this is no problem since removing
+ // a non-registered fd is a no-opp
+ fdDispatcher_.remove(fd, eventMask);
+ fileDispatcher_.remove(fd, eventMask);
+}
+
+// public members
+
prefix_ senf::Scheduler::Scheduler & senf::Scheduler::instance()
{
static Scheduler instance;
return instance;
}
-prefix_ unsigned senf::Scheduler::timeout(ClockService::clock_type timeout,
- SimpleCallback const & cb)
+prefix_ int senf::retrieve_filehandle(int fd)
+{
+ return fd;
+}
+
+prefix_ senf::Scheduler::timer_id senf::Scheduler::timeout(ClockService::clock_type timeout,
+ SimpleCallback const & cb)
+{
+ return timerDispatcher_.add("<anon timer>", timeout, cb);
+}
+
+prefix_ senf::Scheduler::timer_id senf::Scheduler::timeout(std::string const & name,
+ ClockService::clock_type timeout,
+ SimpleCallback const & cb)
{
- ++ timerIdCounter_;
- TimerMap::iterator i (
- timerMap_.insert(std::make_pair(timerIdCounter_,
- TimerSpec(timeout,cb,timerIdCounter_))).first);
- timerQueue_.push(i);
- return timerIdCounter_;
+ return timerDispatcher_.add(name, timeout, cb);
}
-prefix_ void senf::Scheduler::cancelTimeout(unsigned id)
+prefix_ void senf::Scheduler::cancelTimeout(timer_id id)
{
- TimerMap::iterator i (timerMap_.find(id));
- if (i != timerMap_.end())
- i->second.canceled = true;
+ timerDispatcher_.remove(id);
}
prefix_ senf::ClockService::clock_type senf::Scheduler::timeoutEarly()
const
{
- return eventEarly_;
+ SENF_LOG( (senf::log::IMPORTANT)
+ ("timeoutEarly() is deprecated and a no-op. It will be removed") );
+ return 0;
}
prefix_ void senf::Scheduler::timeoutEarly(ClockService::clock_type v)
{
- eventEarly_ = v;
+ SENF_LOG( (senf::log::IMPORTANT)
+ ("timeoutEarly() is deprecated and a no-op. It will be removed") );
}
prefix_ senf::ClockService::clock_type senf::Scheduler::timeoutAdjust()
const
{
- return eventAdjust_;
+ SENF_LOG( (senf::log::IMPORTANT)
+ ("timeoutAdjust() is deprecated and a no-op. It will be removed") );
+ return 0;
}
prefix_ void senf::Scheduler::timeoutAdjust(ClockService::clock_type v)
{
- eventAdjust_ = v;
+ SENF_LOG( (senf::log::IMPORTANT)
+ ("timeoutAdjust() is deprecated and a no-op. It will be removed") );
+}
+
+prefix_ void senf::Scheduler::registerSignal(unsigned signal, SignalCallback const & cb)
+{
+ signalDispatcher_.add(signal, cb);
+}
+
+prefix_ void senf::Scheduler::unregisterSignal(unsigned signal)
+{
+ signalDispatcher_.remove(signal);
}
prefix_ void senf::Scheduler::terminate()
prefix_ senf::ClockService::clock_type senf::Scheduler::eventTime()
const
{
- return eventTime_;
+ return manager_.eventTime();
}
-prefix_ int senf::retrieve_filehandle(int fd)
+prefix_ unsigned senf::Scheduler::hangCount()
+ const
{
- return fd;
+ return runner_.hangCount();
}
-prefix_ senf::Scheduler::TimerSpecCompare::result_type
-senf::Scheduler::TimerSpecCompare::operator()(first_argument_type a, second_argument_type b)
-{
- return a->second < b->second;
-}
+prefix_ senf::Scheduler::Scheduler()
+ : terminate_ (false),
+ fdDispatcher_ (manager_, runner_),
+ timerDispatcher_ (manager_, runner_),
+ signalDispatcher_ (manager_, runner_),
+ fileDispatcher_ (manager_, runner_)
+{}
///////////////////////////////cci.e///////////////////////////////////////
#undef prefix_
}
template <class Handle>
+prefix_ void senf::Scheduler::add(std::string const & name, Handle const & handle,
+ FdCallback const & cb, int eventMask)
+{
+ do_add(name, retrieve_filehandle(handle),cb,eventMask);
+}
+
+template <class Handle>
prefix_ void senf::Scheduler::remove(Handle const & handle, int eventMask)
{
// retrieve_filehandle is found via ADL
#define HH_Scheduler_ 1
// Custom includes
-#include <signal.h>
-#include <map>
-#include <queue>
-#include <boost/function.hpp>
-#include <boost/utility.hpp>
-#include <boost/call_traits.hpp>
-#include <boost/integer.hpp>
-#include "ClockService.hh"
+#include "../Utils/Logger/SenfLog.hh"
+#include "FdDispatcher.hh"
+#include "TimerDispatcher.hh"
+#include "SignalDispatcher.hh"
+#include "FileDispatcher.hh"
#include "../Utils/Logger/SenfLog.hh"
//#include "scheduler.mpp"
/** \brief SENF Project namespace */
namespace senf {
- /** \brief Singleton class to manage the event loop
+ /** \brief Visible scheduler interface
- The %scheduler singleton manages the central event loop. It manages and dispatches all types
- of events managed by the scheduler library:
+ 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
// e.g. in Foo::Foo() constructor:
Scheduler::instance().add(handle_, senf::membind(&Foo::callback, this)), 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.
+
\section sched_fd Registering file descriptors
Scheduler::instance().cancelTimeout(id);
\endcode
Timing is based on the ClockService, which provides a high resolution and strictly
- monotonous time source. 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().
-
- There are two parameters which adjust the exact: \a timeoutEarly and \a timeoutAdjust. \a
- timeoutEarly is the time, a callback may be called before the deadline time is
- reached. Setting this value below the scheduling granularity of the kernel will have the
- %scheduler go into a <em>busy wait</em> (that is, an endless loop consuming 100% of CPU
- recources) until the deadline time is reached! This is seldom desired. The default setting
- of 11ms is adequate in most cases (it's slightly above the lowest linux scheduling
- granularity).
-
- The other timeout scheduling parameter is \a timeoutAdjust. This value will be added to the
- timeout value before calculating the next delay value thereby compensating for \a
- timeoutEarly. By default, this value is set to 0 but may be changed if needed.
+ 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().
\section sched_signals Registering POSIX/UNIX signals
A registered signal does \e not count as 'something to do'. It is therefore not possible to
wait for signals \e only.
- \todo Fix EventId parameter (probably to int) to allow |-ing without casting ...
-
+ \todo Change the Scheduler API to use RAII. Additionally, this will remove all dynamic
+ memory allocations from the scheduler.
\todo Fix the file support to use threads (?) fork (?) and a pipe so it works reliably even
over e.g. NFS.
-
- \todo Add a check in the alarm callback which is already called every x seconds to check,
- that a single callback is not blocking.
*/
class Scheduler
: boost::noncopyable
\li Error flags. These additional flags may be passed to a handler to pass an error
condition to the handler.
*/
- enum EventId {
- EV_NONE = 0 /**< No event */
- , EV_READ = 1 /**< File descriptor is readable */
- , EV_PRIO = 2 /**< File descriptor has OOB data */
- , EV_WRITE = 4 /**< File descriptor is writable */
- , EV_ALL = 7 /**< Used to register all events at once (read/prio/write) */
- , EV_HUP = 8 /**< Hangup condition on file handle */
- , EV_ERR = 16 /**< Error condition on file handle */
+ enum EventId {
+ EV_NONE = 0 /**< No event */
+ , EV_READ = scheduler::FdManager::EV_READ /**< File descriptor is readable */
+ , EV_PRIO = scheduler::FdManager::EV_PRIO /**< File descriptor has OOB data */
+ , EV_WRITE = scheduler::FdManager::EV_WRITE /**< File descriptor is writable */
+ , EV_ALL = scheduler::FdManager::EV_READ
+ | scheduler::FdManager::EV_PRIO
+ | scheduler::FdManager::EV_WRITE /**< Used to register all events at once
+ (read/prio/write) */
+ , EV_HUP = scheduler::FdManager::EV_HUP /**< Hangup condition on file handle */
+ , EV_ERR = scheduler::FdManager::EV_ERR /**< Error condition on file handle */
};
- /** \brief Template typedef for Callback type
-
- This is a template typedef (which does not exist in C++) that is, a template class whose
- sole member is a typedef symbol defining the callback type given the handle type.
-
- The Callback is any callable object taking a \c Handle and an \c EventId as argument.
- \code
- template <class Handle>
- struct GenericCallback {
- typedef boost::function<void (typename boost::call_traits<Handle>::param_type,
- EventId) > Callback;
- };
- \endcode
- */
- typedef boost::function<void (EventId)> FdCallback;
+ /** \brief Callback type for file descriptor events */
+ typedef boost::function<void (int)> FdCallback;
/** \brief Callback type for timer events */
typedef boost::function<void ()> SimpleCallback;
+ /** \brief Callback type for signal events */
+ typedef boost::function<void (siginfo_t const &)> SignalCallback;
+
+ /** \brief Timer id type */
+ typedef scheduler::TimerDispatcher::timer_id timer_id;
+
///////////////////////////////////////////////////////////////////////////
///\name Structors and default members
///@{
This static member is used to access the singleton instance. This member is save to
return a correctly initialized %scheduler instance even if called at global construction
time
-
- \implementation This static member just defines the %scheduler as a static method
- variable. The C++ standard then provides above guarantee. The instance will be
- initialized the first time, the code flow passes the variable declaration found in
- the instance() body.
*/
static Scheduler & instance();
///\{
template <class Handle>
- void add(Handle const & handle, FdCallback const & cb,
- int eventMask = EV_ALL); ///< Add file handle event callback
+ void add(std::string const & name, Handle const & handle, FdCallback const & cb,
+ int eventMask = EV_ALL); ///< Add file handle event callback
/**< add() will add a callback to the %scheduler. The
callback will be called for the given type of event on
the given arbitrary file-descriptor or
handle-like object. If there already is a Callback
registered for one of the events requested, the new
handler will replace the old one.
+ \param[in] name descriptive name to identify the
+ callback.
\param[in] handle file descriptor or handle providing
the Handle interface defined above.
\param[in] cb callback
\param[in] eventMask arbitrary combination via '|'
operator of \ref senf::Scheduler::EventId "EventId"
designators. */
+
+ template <class Handle>
+ void add(Handle const & handle, FdCallback const & cb,
+ int eventMask = EV_ALL); ///< Add file handle event callback
+ /**< \see add() */
+
+
template <class Handle>
void remove(Handle const & handle, int eventMask = EV_ALL); ///< Remove event callback
/**< remove() will remove any callback registered for any of
\param[in] eventMask arbitrary combination via '|'
operator of \ref senf::Scheduler::EventId "EventId"
designators. */
+
///\}
///\name Timeouts
///\{
- unsigned timeout(ClockService::clock_type timeout, SimpleCallback const & cb);
+ timer_id timeout(std::string const & name, ClockService::clock_type timeout,
+ SimpleCallback const & cb);
///< Add timeout event
/**< \returns timer id
+ \param[in] name descriptive name to identify the
+ callback.
\param[in] timeout timeout in nanoseconds
\param[in] cb callback to call after \a timeout
milliseconds */
- void cancelTimeout(unsigned id); ///< Cancel timeout \a id
+ timer_id timeout(ClockService::clock_type timeout, SimpleCallback const & cb);
+ ///< Add timeout event
+ /**< \see timeout() */
+
+ void cancelTimeout(timer_id id); ///< Cancel timeout \a id
+#ifndef DOXYGEN
ClockService::clock_type timeoutEarly() const;
- ///< Fetch the \a timeoutEarly parameter
void timeoutEarly(ClockService::clock_type v);
- ///< Set the \a timeoutEarly parameter
ClockService::clock_type timeoutAdjust() const;
- ///< Fetch the \a timeoutAdjust parameter
void timeoutAdjust(ClockService::clock_type v);
- ///< Set the \a timeoutAdjust parameter
+#endif
///\}
///\name Signal handlers
///\{
- void registerSignal(unsigned signal, SimpleCallback const & cb);
+ void registerSignal(unsigned signal, SignalCallback const & cb);
///< Add signal handler
/**< \param[in] signal signal number to register handler for
\param[in] cb callback to call whenever \a signal is
void unregisterSignal(unsigned signal);
///< Remove signal handler for \a signal
- /// The signal number passed to registerSignal or unregisterSignal is invalid
- struct InvalidSignalNumberException : public senf::Exception
- { InvalidSignalNumberException()
- : senf::Exception("senf::Scheduler::InvalidSignalNumberException"){} };
-
-
///\}
void process(); ///< Event handler main loop
delivered \e not the time it should have been delivered
(in the case of timers). */
+ unsigned hangCount() const;
+
protected:
private:
Scheduler();
void do_add(int fd, FdCallback const & cb, int eventMask = EV_ALL);
- void do_remove(int fd, int eventMask = EV_ALL);
-
- void registerSigHandlers();
- static void sigHandler(int signal, ::siginfo_t * siginfo, void *);
-
-# ifndef DOXYGEN
-
- /** \brief Descriptor event specification
- \internal */
- struct EventSpec
- {
- FdCallback cb_read;
- FdCallback cb_prio;
- FdCallback cb_write;
-
- EventSpec() : file(false) {}
-
- int epollMask() const;
-
- bool file;
- };
-
- /** \brief Timer event specification
- \internal */
- struct TimerSpec
- {
- TimerSpec() : timeout(), cb() {}
- TimerSpec(ClockService::clock_type timeout_, SimpleCallback cb_, unsigned id_)
- : timeout(timeout_), cb(cb_), id(id_), canceled(false) {}
-
- bool operator< (TimerSpec const & other) const
- { return timeout > other.timeout; }
-
- ClockService::clock_type timeout;
- SimpleCallback cb;
- unsigned id;
- bool canceled;
- };
+ void do_add(std::string const & name, int fd, FdCallback const & cb,
+ int eventMask = EV_ALL);
+ void do_remove(int fd, int eventMask);
-# endif
-
- typedef std::map<int,EventSpec> FdTable;
- typedef std::map<unsigned,TimerSpec> TimerMap; // sorted by id
- typedef std::vector<unsigned> FdEraseList;
-
-# ifndef DOXYGEN
-
- struct TimerSpecCompare
- {
- typedef TimerMap::iterator first_argument_type;
- typedef TimerMap::iterator second_argument_type;
- typedef bool result_type;
-
- result_type operator()(first_argument_type a, second_argument_type b);
- };
-
-# endif
-
- typedef std::priority_queue<TimerMap::iterator, std::vector<TimerMap::iterator>,
- TimerSpecCompare> TimerQueue; // sorted by time
-
- typedef std::vector<SimpleCallback> SigHandlers;
-
- FdTable fdTable_;
- FdEraseList fdErase_;
- unsigned files_;
-
- unsigned timerIdCounter_;
- TimerQueue timerQueue_;
- TimerMap timerMap_;
-
- SigHandlers sigHandlers_;
- ::sigset_t sigset_;
- int sigpipe_[2];
-
- int epollFd_;
bool terminate_;
- ClockService::clock_type eventTime_;
- ClockService::clock_type eventEarly_;
- ClockService::clock_type eventAdjust_;
+ scheduler::FdManager manager_;
+ scheduler::FIFORunner runner_;
+
+ scheduler::FdDispatcher fdDispatcher_;
+ scheduler::TimerDispatcher timerDispatcher_;
+ scheduler::SignalDispatcher signalDispatcher_;
+ scheduler::FileDispatcher fileDispatcher_;
};
/** \brief Default file descriptor accessor
int size;
int event;
- void callback(int fd, Scheduler::EventId ev)
+ void callback(int fd, int ev)
{
event = ev;
switch (event & Scheduler::EV_ALL) {
Scheduler::instance().terminate();
}
+ bool timeoutCalled = false;
void timeout()
{
+ timeoutCalled = true;
Scheduler::instance().terminate();
}
return handle.fd_;
}
- void handleCallback(HandleWrapper const & handle, Scheduler::EventId event)
+ void handleCallback(HandleWrapper const & handle, int event)
{
if (handle.tag_ != "TheTag")
return;
ClockService::clock_type sigtime (0);
- void sigusr()
+ void sigusr(siginfo_t const &)
{
sigtime = ClockService::now();
Scheduler::instance().terminate();
}
-
+
void delay(unsigned long milliseconds)
{
struct timespec ts;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
while (nanosleep(&ts,&ts) < 0 && errno == EINTR) ;
}
+
+ void blockingHandler()
+ {
+ delay(1200);
+ Scheduler::instance().terminate();
+ }
+
}
-BOOST_AUTO_UNIT_TEST(scheduler)
+BOOST_AUTO_UNIT_TEST(testScheduler)
{
int pid = start_server();
BOOST_REQUIRE (pid);
buffer[size]=0;
BOOST_CHECK_EQUAL( buffer, "READ" );
+ event = Scheduler::EV_NONE;
BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(
ClockService::now()+ClockService::milliseconds(200),&timeout) );
BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(
ClockService::now()+ClockService::milliseconds(400),&timeout) );
ClockService::clock_type t (ClockService::now());
BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
- BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (t+ClockService::milliseconds(200)) );
+ BOOST_CHECK_PREDICATE( is_close, (ClockService::now()-t) (ClockService::milliseconds(200)) );
+ BOOST_CHECK( timeoutCalled );
+ BOOST_CHECK_EQUAL( event, Scheduler::EV_NONE );
+ BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (Scheduler::instance().eventTime()) );
+ timeoutCalled = false;
+ BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
+ BOOST_CHECK_PREDICATE( is_close, (ClockService::now()-t) (ClockService::milliseconds(400)) );
+ BOOST_CHECK( timeoutCalled );
+ BOOST_CHECK_EQUAL( event, Scheduler::EV_NONE );
+
+ BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(ClockService::now(), &blockingHandler) );
BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
- BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (t+ClockService::milliseconds(400)) );
+ BOOST_CHECK_EQUAL( Scheduler::instance().hangCount(), 1u );
HandleWrapper handle(sock,"TheTag");
BOOST_CHECK_NO_THROW( Scheduler::instance().add(handle,
return;
}
- i = handlers_.insert(std::make_pair(signal, SignalEvent(cb))).first;
+ i = handlers_.insert(std::make_pair(signal, SignalEvent(signal, cb))).first;
sigaddset(&sigSet_, signal);
runner_.enqueue(&i->second);
// Custom includes
#include <unistd.h>
+#include "../Utils/signalnames.hh"
#define prefix_ inline
///////////////////////////////cci.p///////////////////////////////////////
blocked_ = false;
}
+prefix_ bool senf::scheduler::SignalDispatcher::empty()
+ const
+{
+ return handlers_.empty();
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::scheduler::SignalDispatcher::SignalEvent
-prefix_ senf::scheduler::SignalDispatcher::SignalEvent::SignalEvent(Callback cb_)
+prefix_ senf::scheduler::SignalDispatcher::SignalEvent::SignalEvent(int signal, Callback cb_)
: cb (cb_)
-{}
+{
+ name = signalName(signal);
+}
prefix_ void senf::scheduler::SignalDispatcher::SignalEvent::run()
{
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Scheduler dispatcher managing UNIX signals
+
+ This dispatcher supports registering UNIX signals with the Scheduler.
+
+ \implementation SignalDispatcher provides a single signal handler which all registered
+ signals are assigned to. When a signal is received, data is written to a pipe which has
+ been added to the FdManager and this signals the event.
+
+ \todo Add signal block/unblock management to the FdManager to reduce the number of
+ setprocmask() calls
*/
class SignalDispatcher
: public FdManager::Event
///@}
///////////////////////////////////////////////////////////////////////////
- void add(int signal, Callback const & cb);
- void remove(int signal);
+ void add(int signal, Callback const & cb); ///< Add signal event
+ /**< \param[in] signal signal number
+ \param[in] cb Callback */
+
+ void remove(int signal); ///< Unregister signal event
+
+ void unblockSignals(); ///< Unblock registered signals
+ /**< Must be called before waiting for an event */
+ void blockSignals(); ///< Block registered signals
+ /**< Must be called directly after FdManager returns */
- void blockSignals();
- void unblockSignals();
+ bool empty() const; ///< \c true, if no signal is registered.
protected:
private:
+ ///< Internal: UNIX signal event
struct SignalEvent
: public FIFORunner::TaskInfo
{
- explicit SignalEvent(Callback cb_);
+ SignalEvent(int signal, Callback cb_);
virtual void run();
siginfo_t siginfo;
}
+#if 0
+// We can't test this when testing the Scheduler since the Scheduler instance
+// already uses the only SignalDispatcher instance allowed ...
+
BOOST_AUTO_UNIT_TEST(signalDispatcher)
{
senf::scheduler::FdManager manager;
SENF_CHECK_NO_THROW( dispatcher.remove(SIGUSR1) );
}
+#endif
+
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
}
struct sigevent ev;
+ ::memset(&ev, 0, sizeof(ev));
ev.sigev_notify = SIGEV_SIGNAL;
ev.sigev_signo = SIGALRM;
ev.sigev_value.sival_ptr = this;
}
prefix_ senf::scheduler::TimerDispatcher::timer_id
-senf::scheduler::TimerDispatcher::add(ClockService::clock_type timeout, Callback const & cb)
+senf::scheduler::TimerDispatcher::add(std::string const & name,
+ ClockService::clock_type timeout, Callback const & cb)
{
while (timerIdIndex_.find(++lastId_) != timerIdIndex_.end()) ;
- TimerMap::iterator i (timers_.insert(std::make_pair(timeout, TimerEvent(lastId_, cb, *this))));
+ TimerMap::iterator i (
+ timers_.insert(std::make_pair(timeout, TimerEvent(lastId_, cb, *this, name))));
timerIdIndex_.insert(std::make_pair(lastId_, i));
runner_.enqueue(&(i->second));
if (! blocked_)
TimerMap::iterator i (timers_.begin());
TimerMap::iterator const i_end (timers_.end());
- ClockService::clock_type now (ClockService::now());
+ ClockService::clock_type now (manager_.eventTime());
for (; i != i_end && i->first <= now ; ++i)
i->second.runnable = true;
}
prefix_ void senf::scheduler::TimerDispatcher::reschedule()
{
struct itimerspec timer;
+ memset(&timer, 0, sizeof(timer));
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 0;
if (timers_.empty()) {
+ SENF_LOG( (senf::log::VERBOSE)("Timer disabled") );
timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 0;
}
else {
ClockService::clock_type next (timers_.begin()->first);
+ if (next <= 0)
+ next = 1;
timer.it_value.tv_sec = ClockService::in_seconds(next);
timer.it_value.tv_nsec = ClockService::in_nanoseconds(
next - ClockService::seconds(timer.it_value.tv_sec));
+ SENF_LOG( (senf::log::VERBOSE)("Next timeout scheduled @" << timer.it_value.tv_sec << "."
+ << std::setw(9) << std::setfill('0') << timer.it_value.tv_nsec) );
}
if (timer_settime(timerId_, TIMER_ABSTIME, &timer, 0)<0)
SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
}
+///////////////////////////////////////////////////////////////////////////
+// senf::scheduler::TimerDispatcher::TimerEvent
+
+prefix_ void senf::scheduler::TimerDispatcher::TimerEvent::run()
+{
+ Callback savedCb (cb);
+ dispatcher.remove(id);
+ // The member is now running WITHOUT AN OBJECT ... that has been destroyed above !!!!!! On the
+ // other hand, if we do things the other way round, we have no idea, whether the callback might
+ // explicitly remove us and we have the same problem then ...
+ savedCb();
+}
+
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
//#include "TimerDispatcher.mpp"
prefix_ senf::scheduler::TimerDispatcher::TimerEvent::TimerEvent(timer_id id_,
Callback const & cb_,
- TimerDispatcher & dispatcher_)
+ TimerDispatcher & dispatcher_,
+ std::string const & n)
: id (id_), cb (cb_), dispatcher (dispatcher_)
-{}
+{
+ name = n;
+}
-prefix_ void senf::scheduler::TimerDispatcher::TimerEvent::run()
+prefix_ bool senf::scheduler::TimerDispatcher::empty()
+ const
{
- cb();
- dispatcher.remove(id);
+ return timers_.empty();
}
///////////////////////////////cci.e///////////////////////////////////////
#include "ClockService.hh"
#include "FdManager.hh"
#include "FIFORunner.hh"
+#include "../Utils/Logger/SenfLog.hh"
//#include "TimerDispatcher.mpp"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
namespace scheduler {
- /** \brief
+ /** \brief Scheduler dispatcher managing timers
+
+ Timers are implemented using high-precision POSIX real-time timers. As such, the timer
+ granularity is given by clock_getres(CLOCK_MONOTONIC) which is 1ns on current linux kernels.
+
+ \implementation TimerDispatcher manages a single POSIX timer which is always programmed to
+ expire when the next scheduled timer needs to fire. The timer sends a signal (SIGALRM is
+ used). The handler writes data into a pipe which is has been added to the FdManager.
*/
class TimerDispatcher
: public FdManager::Event
{
+ SENF_LOG_CLASS_AREA();
+
public:
///////////////////////////////////////////////////////////////////////////
// Types
///@}
///////////////////////////////////////////////////////////////////////////
- timer_id add(ClockService::clock_type timeout, Callback const & cb);
- void remove(timer_id id);
-
- void blockSignals();
- void unblockSignals();
+ timer_id add(std::string const & name, ClockService::clock_type timeout,
+ Callback const & cb);
+ ///< Add timer event
+ /**< This call adds a new timer expiring at the given point
+ in time.
+ \param[in] name descriptive name
+ \param[in] timeout point in time when the timer is to
+ expire
+ \param[in] cb callback
+ \returns a \c timer_id which can be used to remove the
+ timer. */
+ void remove(timer_id id); ///< Remove timer
+
+ void unblockSignals(); ///< Unblock internal signals
+ /**< Must be called before waiting for an event */
+ void blockSignals(); ///< Block internal signals
+ /**< Must be called directly after the FdManager returns */
+
+ bool empty() const; ///< \c true, if no timer is registered.
protected:
private:
+ /// Internal: Timer event
struct TimerEvent
: public FIFORunner::TaskInfo
{
- TimerEvent(timer_id id_, Callback const & cb_, TimerDispatcher & dispatcher_);
+ TimerEvent(timer_id id_, Callback const & cb_, TimerDispatcher & dispatcher_,
+ std::string const & name);
virtual void run();
timer_id id;
senf::ClockService::clock_type t (senf::ClockService::now());
senf::scheduler::TimerDispatcher::timer_id id;
SENF_CHECK_NO_THROW(
- id = dispatcher.add( t + senf::ClockService::milliseconds(500), &handler ) );
+ id = dispatcher.add( "testTimer", t + senf::ClockService::milliseconds(500), &handler ) );
SENF_CHECK_NO_THROW( dispatcher.unblockSignals() );
SENF_CHECK_NO_THROW( manager.processOnce() );
SENF_CHECK_NO_THROW( dispatcher.blockSignals() );
SENF_CHECK_NO_THROW( runner.run() );
senf::ClockService::clock_type t2 (senf::ClockService::now());
BOOST_CHECK( called );
- BOOST_CHECK_PREDICATE( is_close, (t2)(t + senf::ClockService::milliseconds(500)) );
+ BOOST_CHECK_PREDICATE( is_close, (t2-t)(senf::ClockService::milliseconds(500)) );
SENF_CHECK_NO_THROW( dispatcher.remove(id) );
+
+ called=false;
+ t = senf::ClockService::now();
+ SENF_CHECK_NO_THROW( dispatcher.add( "testTimer", t, &handler ) );
+ SENF_CHECK_NO_THROW( dispatcher.unblockSignals() );
+ SENF_CHECK_NO_THROW( manager.processOnce() );
+ SENF_CHECK_NO_THROW( dispatcher.blockSignals() );
+ SENF_CHECK_NO_THROW( runner.run() );
+ BOOST_CHECK_PREDICATE( is_close, (t) (senf::ClockService::now()) );
+ BOOST_CHECK( called );
}
///////////////////////////////cc.e////////////////////////////////////////
#include "../Exception.hh"
#include "../membind.hh"
#include "../Backtrace.hh"
+#include "../signalnames.hh"
// #define __USE_GNU
#include <ucontext.h>
namespace {
void fatalSignalsHandler(int sig, ::siginfo_t * info, void * arg)
{
- static char const * const signames[] = {
- "",
- "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
- "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM",
- "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU",
- "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
- "SIGPWR", "SIGSYS" };
-
// ::ucontext_t * ucontext = static_cast<ucontext_t*>(arg);
- std::cerr << "\n" << "Signal " << sig;
- if (unsigned(sig) < sizeof(signames) / sizeof(signames[0]))
- std::cerr << " (" << signames[unsigned(sig)] << ")";
- std::cerr << " received\n";
+ std::cerr << "\n" << "Signal " << senf::signalName(sig) << '(' << sig << ')'
+ << " received\n";
if (sig == SIGSEGV)
std::cerr << "Invalid memory access at " << info->si_addr << "\n";
}
}
-prefix_ void senf::detail::DaemonWatcher::sigChld()
+prefix_ void senf::detail::DaemonWatcher::sigChld(siginfo_t const &)
{
sigChld_ = true;
if (coutpipe_ == -1 && cerrpipe_ == -1)
targets_.push_back(target);
}
-prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId event)
+prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(int event)
{
char buf[1024];
int n (0);
buffer_.insert(buffer_.end(), buf, buf+n);
}
-prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(Scheduler::EventId event,
+prefix_ void senf::detail::DaemonWatcher::Forwarder::writeData(int event,
Targets::iterator target)
{
if (event != Scheduler::EV_WRITE) {
};
typedef std::list<Target> Targets;
- void readData(Scheduler::EventId event);
- void writeData(Scheduler::EventId event, Targets::iterator target);
+ void readData(int event);
+ void writeData(int event, Targets::iterator target);
Buffer buffer_;
int src_;
};
void pipeClosed(int id);
- void sigChld();
+ void sigChld(siginfo_t const &);
void childDied();
void childOk();
#include <boost/filesystem/operations.hpp>
#include "Daemon.hh"
#include "../Utils/Exception.hh"
+#include "../Utils/Backtrace.hh"
#include "../Utils/auto_unit_test.hh"
#include <boost/test/test_tools.hpp>
int pid;
+ void backtrace(int)
+ {
+ senf::backtrace(std::cerr, 100);
+ ::signal(SIGABRT, SIG_DFL);
+ ::kill(::getpid(), SIGABRT);
+ };
+
int run(int argc, char ** argv)
{
pid = ::fork();
if (pid < 0) throw senf::SystemException("::fork()");
if (pid == 0) {
+ signal(SIGABRT, &backtrace);
try {
::_exit(myMain(argc, argv));
} catch (std::exception & ex) {
char sep (' ');
for (; i != i_end; ++i) {
- stream_ << timestamp << sep;
+ stream_ << senf::ClockService::abstime(timestamp) << sep;
stream_ << "[" << LEVELNAMES_[level] << "]";
if (area != "senf::log::DefaultArea")
- stream_ << "[" << area << "]";
+ stream_ << " [" << area << "]";
stream_ << " " << *i << "\n";
sep = '-';
}
prefix_ senf::log::time_type senf::log::SystemTimeSource::operator()()
const
{
- struct ::timespec tm;
- if (::clock_gettime(CLOCK_MONOTONIC, &tm) < 0)
- SENF_THROW_SYSTEM_EXCEPTION("::clock_gettime()");
- return static_cast<time_type>(tm.tv_sec)*1000000000ll+tm.tv_nsec;
+ return senf::ClockService::now();
}
///////////////////////////////cc.e////////////////////////////////////////
// Custom includes
#include <boost/cstdint.hpp>
#include <memory>
+#include "../../Scheduler/ClockService.hh"
//#include "TimeSource.mpp"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
namespace log {
- typedef boost::int_fast64_t time_type;
+ typedef ClockService::clock_type time_type;
/** \brief Log message time source abstract base class
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file
- \brief Dispatcher inline non-template implementation */
+ \brief signalnames non-inline non-template implementation */
-//#include "Dispatcher.ih"
+#include "signalnames.hh"
+//#include "signalnames.ih"
// Custom includes
+#include <boost/format.hpp>
-#define prefix_ inline
-///////////////////////////////cci.p///////////////////////////////////////
+//#include "signalnames.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
-prefix_ senf::scheduler::Dispatcher::Dispatcher()
- : fdDispatcher_ (fdManager_, runner_)
-{}
+prefix_ std::string const & senf::signalName(int signal)
+{
+ static std::string const names[] = {
+ "<unknown>",
+ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE",
+ "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM",
+ "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU",
+ "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
+ "SIGPWR", "SIGSYS" };
-///////////////////////////////cci.e///////////////////////////////////////
+ return names[
+ (signal <= 0 || signal > int(sizeof(names)/sizeof(names[0]))) ? 0 : signal ];
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
+//#include "signalnames.mpp"
\f
// Local Variables:
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file
- \brief Dispatcher inline non-template implementation */
+ \brief signalnames public header */
-//#include "Dispatcher.ih"
+#ifndef HH_signalnames_
+#define HH_signalnames_ 1
// Custom includes
+#include <string>
-#define prefix_ inline
-///////////////////////////////cci.p///////////////////////////////////////
+//#include "signalnames.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
-prefix_ senf::scheduler::Dispatcher::Dispatcher()
- : fdDispatcher_ (fdManager_, runner_)
-{}
+namespace senf {
-///////////////////////////////cci.e///////////////////////////////////////
-#undef prefix_
+ std::string const & signalName(int signal);
+
+}
+
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "signalnames.cci"
+//#include "signalnames.ct"
+//#include "signalnames.cti"
+#endif
\f
// Local Variables: