Utils/Console: Fix singleton instantiation order (ServerManager / Scheduler)
g0dil [Wed, 9 Jul 2008 14:16:12 +0000 (14:16 +0000)]
Scheduler: Add automatic periodic resync to ClockService::abstime() and ClockService::clock()
Scheduler: Add task nameing
Scheduler: BUGFIX: Add missing task-dequeue in FdDispatcher::add()
Scheduler: Add empty() member to all dispatchers
Scheduler: Add eventTime() support to FdManager
Scheduler: Add support for non-pollable file descriptor detection to FdManager and FdDispatcher
Scheduler: Add FIFORunner task watchdog
Scheduler: Add debugging backtrace to FIFORunner
Scheduler: Change senf::Scheduler implementation to use the new dispatcher infrastructure
Scheduler: BUGFIX: Fix access to (possibly) destroyed object in TimerEvent::run()
Utils: Add senf::signalName() helper
Utils/Daemon: Use senf::signalName() in debug messages
Utils/Logger: Fix logger to correctly format the timestamp (the deadlock should be fixed now)
Update scheduler callback signatures to the new API throughout the project
Update documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@892 270642c3-0616-0410-b53a-bc976706d245

56 files changed:
Console/Readline.cc
Console/Readline.hh
Console/Server.cc
Console/Server.cci
Console/Server.hh
Console/Server.ih
Examples/MCSniffer/MCSniffer.cc
Examples/Sniffer/Sniffer.cc
Examples/TCPClientServer/server.cc
Examples/UDPClientServer/udpServer.cc
PPI/ActiveFeeder.hh
PPI/IOEvent.cc
PPI/IOEvent.hh
PPI/IdleEvent.cc
Scheduler/ClockService.cci
Scheduler/ClockService.hh
Scheduler/FIFORunner.cc
Scheduler/FIFORunner.cci
Scheduler/FIFORunner.hh
Scheduler/FdDispatcher.cc
Scheduler/FdDispatcher.cci
Scheduler/FdDispatcher.hh
Scheduler/FdDispatcher.ih
Scheduler/FdDispatcher.test.cc
Scheduler/FdManager.cc
Scheduler/FdManager.cci
Scheduler/FdManager.hh
Scheduler/FileDispatcher.cc
Scheduler/FileDispatcher.cci
Scheduler/FileDispatcher.hh
Scheduler/FileDispatcher.test.cc
Scheduler/Mainpage.dox
Scheduler/Poller.hh
Scheduler/ReadHelper.ct
Scheduler/ReadHelper.hh
Scheduler/Scheduler.cc
Scheduler/Scheduler.cci
Scheduler/Scheduler.cti
Scheduler/Scheduler.hh
Scheduler/Scheduler.test.cc
Scheduler/SignalDispatcher.cc
Scheduler/SignalDispatcher.cci
Scheduler/SignalDispatcher.hh
Scheduler/SignalDispatcher.test.cc
Scheduler/TimerDispatcher.cc
Scheduler/TimerDispatcher.cci
Scheduler/TimerDispatcher.hh
Scheduler/TimerDispatcher.test.cc
Utils/Daemon/Daemon.cc
Utils/Daemon/Daemon.ih
Utils/Daemon/Daemon.test.cc
Utils/Logger/IOStreamTarget.cc
Utils/Logger/TimeSource.cc
Utils/Logger/TimeSource.hh
Utils/signalnames.cc [copied from Scheduler/Dispatcher.cci with 55% similarity]
Utils/signalnames.hh [moved from Scheduler/Dispatcher.cci with 73% similarity]

index 094c8e4..f1597d4 100644 (file)
@@ -198,7 +198,7 @@ prefix_ void senf::console::detail::ReadlineClientReader::v_translate(std::strin
     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) {
index 00372d7..09a9e61 100644 (file)
@@ -72,7 +72,7 @@ namespace detail {
         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_;
index dc169fa..3ef80ba 100644 (file)
@@ -104,7 +104,7 @@ prefix_ senf::console::Server::~Server()
     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));
@@ -200,7 +200,7 @@ prefix_ void senf::console::detail::NoninteractiveClientReader::v_translate(std:
 {}
 
 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())
index e4601f6..81aaf6d 100644 (file)
@@ -43,6 +43,12 @@ prefix_ void senf::console::detail::ServerManager::remove(ptr server)
     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
 
index 1756e14..b7684d5 100644 (file)
@@ -129,7 +129,7 @@ namespace console {
 
         static Server & start(ServerHandle handle);
 
-        void newClient(Scheduler::EventId event);
+        void newClient(int event);
         void removeClient(Client & client);
         
         ServerHandle handle_;
index 925fd64..3ca80a1 100644 (file)
@@ -42,7 +42,6 @@ namespace console {
 namespace detail {
 
     class ServerManager
-        : public senf::singleton<ServerManager>
     {
     public:
         typedef boost::intrusive_ptr<Server> ptr;
@@ -53,6 +52,8 @@ namespace detail {
         static void add(ptr server);
         static void remove(ptr server);
 
+        static ServerManager & instance();
+
         typedef std::set<ptr> Servers;
         Servers servers_;
 
@@ -170,7 +171,7 @@ namespace detail {
         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_;
index 896ef3a..211ebaf 100644 (file)
@@ -57,7 +57,7 @@ public:
     }
 
 private:
-    void dumpPacket(senf::Scheduler::EventId event)
+    void dumpPacket(int event)
     {
         std::string data (sock.read());
         senf::EthernetPacket packet (
index fbeeab4..7f74806 100644 (file)
@@ -82,7 +82,7 @@ public:
     }
 
 private:
-    void dumpPacket(senf::Scheduler::EventId event)
+    void dumpPacket(int event)
     {
         senf::EthernetPacket packet (
             senf::EthernetPacket::create(senf::noinit));
index 93fb1ba..4dbf274 100644 (file)
@@ -50,7 +50,7 @@ public:
     }
 
 private:
-    void accept(senf::Scheduler::EventId event)
+    void accept(int event)
     {
         senf::TCPv4ClientSocketHandle clientSock (serverSock.accept());
         senf::Scheduler::instance().add(
@@ -59,7 +59,7 @@ private:
             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);
index 091b6ba..d549a92 100644 (file)
@@ -44,7 +44,7 @@ public:
     }
 
 private:
-    void readFromClient(senf::Scheduler::EventId event)
+    void readFromClient(int event)
     {
         std::string data (serverSock.read());
         std::cout << "> " << data<<std::endl ;
index f9c7ed7..494f1ea 100644 (file)
@@ -40,7 +40,7 @@ namespace module {
 
     /** \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.
 
index c39d57a..73bcc5f 100644 (file)
@@ -51,7 +51,7 @@ prefix_ void senf::ppi::IOEvent::v_disable()
     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)
index d2f9528..77c5d26 100644 (file)
@@ -108,7 +108,7 @@ namespace ppi {
         virtual void v_enable();
          virtual void v_disable();
         
-        void cb(Scheduler::EventId event);
+        void cb(int event);
 
         int fd_;
         unsigned events_;
index d334685..37077bb 100644 (file)
@@ -28,6 +28,7 @@
 
 // Custom includes
 #include "../Scheduler/Scheduler.hh"
+#include "../Utils/Logger/SenfLog.hh"
 
 //#include "IdleEvent.mpp"
 #define prefix_
index c24b736..3c15955 100644 (file)
@@ -47,6 +47,8 @@ prefix_ senf::ClockService::clock_type senf::ClockService::now()
 
 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() );
@@ -54,6 +56,8 @@ prefix_ senf::ClockService::clock_type senf::ClockService::clock_m(abstime_type
 
 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
index da50573..5b02c19 100644 (file)
@@ -43,55 +43,21 @@ namespace senf {
     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>
@@ -106,6 +72,11 @@ namespace senf {
             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
@@ -157,7 +128,10 @@ namespace senf {
         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();
index 169bcf8..10cfdc2 100644 (file)
 //#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
@@ -85,10 +119,21 @@ prefix_ void senf::scheduler::FIFORunner::run()
     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);
@@ -97,10 +142,27 @@ prefix_ void senf::scheduler::FIFORunner::run()
         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"
index 5247d5d..1061f4d 100644 (file)
 //#include "FIFORunner.ih"
 
 // Custom includes
+#ifdef SENF_DEBUG
+#include <sstream>
+#include "../Utils/Backtrace.hh"
+#endif
 
 #define prefix_ inline
 ///////////////////////////////cci.p///////////////////////////////////////
@@ -37,13 +41,20 @@ prefix_ senf::scheduler::FIFORunner::TaskInfo::TaskInfo()
 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///////////////////////////////////////
index 3ad8d7e..9121445 100644 (file)
@@ -27,6 +27,7 @@
 #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
@@ -55,14 +63,26 @@ namespace scheduler {
         ///////////////////////////////////////////////////////////////////////////
         // 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
         };
 
         ///////////////////////////////////////////////////////////////////////////
@@ -70,20 +90,35 @@ namespace scheduler {
         ///@{
 
         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_;
     };
 
 
index 7d033a3..4103b51 100644 (file)
@@ -46,10 +46,11 @@ prefix_ senf::scheduler::FdDispatcher::~FdDispatcher()
     }
 }
 
-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()) {
@@ -60,11 +61,28 @@ prefix_ void senf::scheduler::FdDispatcher::add(int fd, Callback const & cb, int
     }
     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)
@@ -77,9 +95,18 @@ 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) {
@@ -106,7 +133,7 @@ prefix_ void senf::scheduler::FdDispatcher::FdEvent::signal(int e)
     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;
index 2222cee..d7c1034 100644 (file)
@@ -37,6 +37,12 @@ prefix_ senf::scheduler::FdDispatcher::FdDispatcher(FdManager & manager, FIFORun
     : manager_ (manager), runner_ (runner)
 {}
 
+prefix_ bool senf::scheduler::FdDispatcher::empty()
+    const
+{
+    return fds_.empty();
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::scheduler::FdDispatcher::FdEvent
 
index 696be2a..affc61d 100644 (file)
@@ -38,7 +38,9 @@
 namespace senf {
 namespace scheduler {
 
-    /** \brief
+    /** \brief Scheduler dispatcher managing poll-able file descriptors
+
+        File descriptors are added directly to the event loop.
       */
     class FdDispatcher
     {
@@ -64,12 +66,27 @@ namespace scheduler {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        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>,
index 81ea330..42d3c85 100644 (file)
@@ -34,6 +34,11 @@ namespace senf {
 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
index 2a87e71..853d862 100644 (file)
@@ -185,8 +185,8 @@ BOOST_AUTO_UNIT_TEST(fdDispatcher)
         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() );
@@ -197,8 +197,8 @@ BOOST_AUTO_UNIT_TEST(fdDispatcher)
 
     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() );
index 323825d..db17838 100644 (file)
@@ -35,6 +35,7 @@
 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);
 }
index 5f7c175..467a97a 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // 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)
@@ -54,6 +58,12 @@ prefix_ int senf::scheduler::FdManager::timeout()
     return poller_.timeout();
 }
 
+prefix_ senf::ClockService::clock_type senf::scheduler::FdManager::eventTime()
+    const
+{
+    return eventTime_;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::scheduler::FdManager::Event
 
index ecc7f98..4d7b05e 100644 (file)
@@ -28,6 +28,7 @@
 
 // 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
     {
@@ -43,9 +58,10 @@ namespace scheduler {
         ///////////////////////////////////////////////////////////////////////////
         // 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 { 
@@ -57,21 +73,41 @@ namespace scheduler {
         ///\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_;
     };
 
 }}
index 1989c90..780335c 100644 (file)
@@ -47,7 +47,8 @@ prefix_ senf::scheduler::FileDispatcher::~FileDispatcher()
     }
 }
 
-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;
@@ -60,8 +61,14 @@ prefix_ void senf::scheduler::FileDispatcher::add(int fd, Callback const & cb, i
     }
     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);
 }
index 7d932d1..a18ea27 100644 (file)
@@ -51,6 +51,12 @@ prefix_ int senf::scheduler::FileDispatcher::timeout()
     return managerTimeout_;
 }
 
+prefix_ bool senf::scheduler::FileDispatcher::empty()
+    const
+{
+    return files_.empty();
+}
+
 ///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
 
index cd4b884..4bff54d 100644 (file)
 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
     {
@@ -64,17 +71,37 @@ namespace scheduler {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
         
-        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>
index 045f125..f923616 100644 (file)
@@ -66,7 +66,8 @@ BOOST_AUTO_UNIT_TEST(fileDispatcher)
     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() );
index e2d5f54..40f3f0e 100644 (file)
 // 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:
@@ -51,4 +136,5 @@ namespace senf {
 // ispell-local-dictionary: "american"
 // mode: flyspell
 // mode: auto-fill
+// compile-command: "scons -U doc"
 // End:
index 0dc5504..0bedf72 100644 (file)
 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
@@ -75,12 +81,17 @@ namespace scheduler {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        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_;
index 981d970..42d068a 100644 (file)
@@ -57,8 +57,7 @@ prefix_ void senf::ReadHelper<Handle>::revoke()
 
 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
@@ -66,8 +65,7 @@ senf::ReadHelper<Handle>::dispatchProcess(ptr helper, Handle handle,
 }
 
 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)
index 34246e2..d43afd7 100644 (file)
@@ -131,8 +131,8 @@ namespace senf {
 
         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_;
index 3d61946..02364a4 100644 (file)
     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();
     }
 }
 
index f436274..f8a565e 100644 (file)
 //#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()
@@ -85,19 +131,22 @@ 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_
index 1404615..aff6271 100644 (file)
@@ -40,6 +40,13 @@ prefix_ void senf::Scheduler::add(Handle const & handle, FdCallback const & cb,
 }
 
 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
index 695e9b3..89e9d23 100644 (file)
 #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
@@ -87,7 +84,10 @@ namespace senf {
         // 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
         
@@ -125,20 +125,9 @@ namespace senf {
         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
@@ -155,13 +144,10 @@ namespace senf {
         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
@@ -181,35 +167,31 @@ namespace senf {
             \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
         ///@{
@@ -225,11 +207,6 @@ namespace senf {
             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();
 
@@ -240,20 +217,29 @@ namespace senf {
         ///\{
 
         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
@@ -264,36 +250,42 @@ namespace senf {
                                              \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
@@ -302,12 +294,6 @@ namespace senf {
         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
@@ -328,92 +314,26 @@ namespace senf {
                                              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
index 5da6b8c..0501762 100644 (file)
@@ -141,7 +141,7 @@ namespace {
     int size;
     int event;
 
-    void callback(int fd, Scheduler::EventId ev)
+    void callback(int fd, int ev)
     {
         event = ev;
         switch (event & Scheduler::EV_ALL) {
@@ -160,8 +160,10 @@ namespace {
         Scheduler::instance().terminate();
     }
 
+    bool timeoutCalled = false;
     void timeout()
     {
+        timeoutCalled = true;
         Scheduler::instance().terminate();
     }
 
@@ -177,7 +179,7 @@ namespace {
         return handle.fd_;
     }
 
-    void handleCallback(HandleWrapper const & handle, Scheduler::EventId event)
+    void handleCallback(HandleWrapper const & handle, int event)
     {
         if (handle.tag_ != "TheTag")
             return;
@@ -191,12 +193,12 @@ namespace {
     
     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;
@@ -204,9 +206,16 @@ namespace {
         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);
@@ -239,15 +248,26 @@ BOOST_AUTO_UNIT_TEST(scheduler)
     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,
index 490c2c2..69d72bc 100644 (file)
@@ -68,7 +68,7 @@ prefix_ void senf::scheduler::SignalDispatcher::add(int signal, Callback const &
         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);
 
index 088fc92..84fe9ab 100644 (file)
@@ -27,6 +27,7 @@
 
 // Custom includes
 #include <unistd.h>
+#include "../Utils/signalnames.hh"
 
 #define prefix_ inline
 ///////////////////////////////cci.p///////////////////////////////////////
@@ -45,12 +46,20 @@ prefix_ void senf::scheduler::SignalDispatcher::unblockSignals()
     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()
 {
index b2c44a4..2c2b7ae 100644 (file)
 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
@@ -59,19 +68,27 @@ namespace scheduler {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        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;
index d0bd0da..7531aae 100644 (file)
@@ -45,6 +45,10 @@ namespace {
 
 }
 
+#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;
@@ -65,6 +69,8 @@ BOOST_AUTO_UNIT_TEST(signalDispatcher)
     SENF_CHECK_NO_THROW( dispatcher.remove(SIGUSR1) );
 }
 
+#endif
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 
index 225e608..520500d 100644 (file)
@@ -56,6 +56,7 @@ prefix_ senf::scheduler::TimerDispatcher::TimerDispatcher(FdManager & manager,
     }
 
     struct sigevent ev;
+    ::memset(&ev, 0, sizeof(ev));
     ev.sigev_notify = SIGEV_SIGNAL;
     ev.sigev_signo = SIGALRM;
     ev.sigev_value.sival_ptr = this;
@@ -84,10 +85,12 @@ prefix_ senf::scheduler::TimerDispatcher::~TimerDispatcher()
 }
 
 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_)
@@ -132,7 +135,7 @@ prefix_ void senf::scheduler::TimerDispatcher::signal(int events)
 
     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;
 }
@@ -152,22 +155,41 @@ prefix_ void senf::scheduler::TimerDispatcher::sigHandler(int signal, ::siginfo_
 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"
index d0a3308..6e8e9e6 100644 (file)
 
 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///////////////////////////////////////
index 1a90ed5..7b6f92a 100644 (file)
@@ -33,6 +33,7 @@
 #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
@@ -62,19 +72,35 @@ namespace scheduler {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        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;
index 46e9a82..9f0599c 100644 (file)
@@ -60,16 +60,26 @@ BOOST_AUTO_UNIT_TEST(timerDispatcher)
     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////////////////////////////////////////
index 94d95ea..708faca 100644 (file)
@@ -43,6 +43,7 @@
 #include "../Exception.hh"
 #include "../membind.hh"
 #include "../Backtrace.hh"
+#include "../signalnames.hh"
 
 // #define __USE_GNU
 #include <ucontext.h>
@@ -487,19 +488,9 @@ prefix_ bool senf::Daemon::pidfileCreate()
 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";
@@ -605,7 +596,7 @@ prefix_ void senf::detail::DaemonWatcher::pipeClosed(int id)
     }
 }
 
-prefix_ void senf::detail::DaemonWatcher::sigChld()
+prefix_ void senf::detail::DaemonWatcher::sigChld(siginfo_t const &)
 {
     sigChld_ = true;
     if (coutpipe_ == -1 && cerrpipe_ == -1)
@@ -658,7 +649,7 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::addTarget(int fd)
     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);
@@ -692,7 +683,7 @@ prefix_ void senf::detail::DaemonWatcher::Forwarder::readData(Scheduler::EventId
     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) {
index 96b375c..0cc5c17 100644 (file)
@@ -70,8 +70,8 @@ namespace detail {
             };
             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_;
@@ -82,7 +82,7 @@ namespace detail {
         };
         
         void pipeClosed(int id);
-        void sigChld();
+        void sigChld(siginfo_t const &);
         void childDied();
         void childOk();
 
index 57a5115..7591434 100644 (file)
@@ -35,6 +35,7 @@
 #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>
@@ -79,11 +80,19 @@ namespace {
 
     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) {
index 487e508..7a57ec2 100644 (file)
@@ -71,10 +71,10 @@ prefix_ void senf::log::IOStreamTarget::v_write(time_type timestamp,
     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 = '-';
     }
index 61b7253..375cce6 100644 (file)
@@ -46,10 +46,7 @@ prefix_ senf::log::TimeSource::~TimeSource()
 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////////////////////////////////////////
index f88bb3b..ed2c490 100644 (file)
@@ -29,6 +29,7 @@
 // Custom includes
 #include <boost/cstdint.hpp>
 #include <memory>
+#include "../../Scheduler/ClockService.hh"
 
 //#include "TimeSource.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -36,7 +37,7 @@
 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
 
similarity index 55%
copy from Scheduler/Dispatcher.cci
copy to Utils/signalnames.cc
index 9364c47..4fc09d2 100644 (file)
 // 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:
similarity index 73%
rename from Scheduler/Dispatcher.cci
rename to Utils/signalnames.hh
index 9364c47..d052e86 100644 (file)
 // 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: