Scheduler: Move timing related code into a ClockService class
g0dil [Mon, 20 Aug 2007 08:47:11 +0000 (08:47 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@398 270642c3-0616-0410-b53a-bc976706d245

Scheduler/ClockService.cci [new file with mode: 0644]
Scheduler/ClockService.hh [new file with mode: 0644]
Scheduler/Scheduler.cc
Scheduler/Scheduler.cci
Scheduler/Scheduler.hh
Scheduler/Scheduler.test.cc

diff --git a/Scheduler/ClockService.cci b/Scheduler/ClockService.cci
new file mode 100644 (file)
index 0000000..9628ec6
--- /dev/null
@@ -0,0 +1,86 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief ClockService inline non-template implementation */
+
+// Custom includes
+#include "boost/date_time/posix_time/posix_time_types.hpp"
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::ClockService
+
+prefix_ senf::ClockService::~ClockService()
+{
+    
+}
+
+prefix_ senf::ClockService::clock_type senf::ClockService::now()
+{
+    return clock(boost::posix_time::microsec_clock::universal_time());
+}
+
+prefix_ senf::ClockService::abstime_type senf::ClockService::abstime(clock_type clock)
+{
+#ifdef BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
+    return instance().base_ + boost::posix_time::nanoseconds(clock);
+#else
+    return instance().base_ + boost::posix_time::microseconds((clock+500)/1000);
+#endif
+}
+
+prefix_ senf::ClockService::clock_type senf::ClockService::clock(abstime_type time)
+{
+    boost::posix_time::time_duration delta (time - instance().base_);
+    return clock_type( delta.ticks() )
+        * clock_type( 1000000000UL / boost::posix_time::time_duration::ticks_per_second() );
+}
+
+////////////////////////////////////////
+// private members
+
+prefix_ senf::ClockService::ClockService()
+    : base_ (boost::posix_time::microsec_clock::universal_time())
+{}
+
+prefix_ senf::ClockService & senf::ClockService::instance()
+{
+    static ClockService instance;
+    return instance;
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
diff --git a/Scheduler/ClockService.hh b/Scheduler/ClockService.hh
new file mode 100644 (file)
index 0000000..6ad51b5
--- /dev/null
@@ -0,0 +1,152 @@
+// $Id$
+//
+// Copyright (C) 2007 
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief ClockService public header */
+
+#ifndef HH_ClockService_
+#define HH_ClockService_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+//#include "ClockService.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+
+    // Implementation note:
+    //
+    // 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 in within now()
+    //
+    // In this case, we use getitimer() to find the time remaining in the timer. Using this value and
+    // an the saved gettimeofday() value we can adjust base_ accordingly.
+    //
+    // b) Clock skew detected in the signal handler
+    //
+    // In this case we use the save gettimeofday() value + CheckInterval to adjust base_.
+    
+    /** \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.
+
+        \fixme Implement the clock-skew detection
+      */
+    class ClockService
+        : boost::noncopyable
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        /** \brief ClockService timer data type
+            
+            Unsigned integer type representing scheduler time. Scheduler time is measured in
+            nanoseconds relative to some implementation defined reference time.
+         */
+        typedef boost::uint_fast64_t clock_type;
+
+        /** \brief Absolute time data type
+
+            Boost.DateTime datatype used to represent absolute date/time values.
+         */
+        typedef boost::posix_time::ptime abstime_type;
+
+        static unsigned const CheckInterval = 1;
+
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        ~ClockService();
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+        static clock_type now();  ///< Return current clock value
+        
+        static abstime_type abstime(clock_type clock); ///< Convert clock to absolute time
+                                        /**< This member converts a clock value into an absolute
+                                             Boost.DateTime value.
+                                             \note You should not base timeout calculations on this
+                                                 absolute time value. Clock time is guaranteed to be
+                                                 monotonous, absolute time may be non-monotonous if
+                                                 the system date/time is changed. */
+
+        static clock_type clock(abstime_type time); ///< Convert absolute time to clock value
+                                        /**< This member converst an absolute time value into the
+                                             corresponding clock value.
+                                             \see abstime */
+
+    protected:
+
+    private:
+        ClockService();
+
+        static ClockService & instance();
+        
+        boost::posix_time::ptime base_;
+    };
+
+
+}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "ClockService.cci"
+//#include "ClockService.ct"
+//#include "ClockService.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 799c4a5..816e663 100644 (file)
@@ -91,14 +91,13 @@ prefix_ senf::Scheduler::Scheduler & senf::Scheduler::instance()
     return instance;
 }
 
-prefix_ void senf::Scheduler::timeout(sched_time timeout, TimerCallback const & cb)
+prefix_ void senf::Scheduler::timeout(ClockService::clock_type timeout, TimerCallback const & cb)
 {
-    timerQueue_.push(TimerSpec(now()+timeout,cb));
+    timerQueue_.push(TimerSpec(ClockService::now()+timeout,cb));
 }
 
 prefix_ senf::Scheduler::Scheduler()
-    : epollFd_ (epoll_create(EPollInitialSize)), 
-      epoch_ (boost::posix_time::microsec_clock::universal_time())
+    : epollFd_ (epoll_create(EPollInitialSize))
 {
     if (epollFd_<0)
         throw SystemException(errno);
@@ -166,7 +165,7 @@ prefix_ void senf::Scheduler::process()
 {
     terminate_ = false;
     while (! terminate_) {
-        sched_time timeNow = now();
+        ClockService::clock_type timeNow = ClockService::now();
 
         while ( ! timerQueue_.empty() && timerQueue_.top().timeout <= timeNow ) {
             timerQueue_.top().cb();
@@ -178,7 +177,7 @@ prefix_ void senf::Scheduler::process()
 
         int timeout (MinTimeout);
         if (! timerQueue_.empty()) {
-            sched_time delta ((timerQueue_.top().timeout - timeNow)/1000000UL);
+            ClockService::clock_type delta ((timerQueue_.top().timeout - timeNow)/1000000UL);
             if (delta<MinTimeout)
                 timeout = int(delta);
         }
index 1c1e7ef..2308705 100644 (file)
@@ -36,15 +36,6 @@ prefix_ void senf::Scheduler::terminate()
     terminate_ = true;
 }
 
-prefix_ senf::Scheduler::sched_time senf::Scheduler::now()
-    const
-{
-    boost::posix_time::time_duration delta (
-        boost::posix_time::microsec_clock::universal_time() - epoch_);
-    return sched_time( delta.ticks() )
-        * sched_time( 1000000000UL / boost::posix_time::time_duration::ticks_per_second() );
-}
-
 prefix_ int senf::retrieve_filehandle(int fd)
 {
     return fd;
index ba077e4..4914e59 100644 (file)
@@ -34,7 +34,7 @@
 #include <boost/utility.hpp>
 #include <boost/call_traits.hpp>
 #include <boost/integer.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "ClockService.hh"
 
 //#include "scheduler.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -93,19 +93,6 @@ namespace senf {
                                           EventId) > Callback;
         };
 
-        /** \brief Scheduler time data type
-            
-            Unsigned integer type representing scheduler time. Scheduler time is measured in
-            nanoseconds relative to some implementation defined reference time.
-         */
-        typedef boost::uint_fast64_t sched_time;
-
-        /** \brief Absolute time data type
-
-            Boost.DateTime datatype used to represent absolute date/time values.
-         */
-        typedef boost::posix_time::ptime abs_time;
-
         /** \brief Callback type for timer events */
         typedef boost::function<void ()> TimerCallback;
 
@@ -160,7 +147,8 @@ namespace senf {
                                              \param[in] eventMask arbitrary combination via '|'
                                                  operator of EventId designators. */
 
-        void timeout(sched_time timeout, TimerCallback const & cb); ///< Add timeout event
+        void timeout(ClockService::clock_type timeout, TimerCallback const & cb); 
+                                        ///< Add timeout event
                                         /**< \param[in] timeout timeout in nanoseconds
                                              \param[in] cb callback to call after \a timeout
                                                  milliseconds
@@ -178,25 +166,7 @@ namespace senf {
                                              it's caller after the currently running callback
                                              returns. */
         
-        abs_time abstime(sched_time time) const; ///< Convert scheduler time to absolute time
-                                        /**< This member converts a scheduler time value into an
-                                             absolute Boost.DateTime value. 
-                                             \note You should not base timeout calculations on this
-                                                 absolute time value. Scheduler time is guaranteed
-                                                 to be monotonous, absolute time may be
-                                                 non-monotonous if the system date/time is
-                                                 changed. */
-
-        sched_time schedtime(abs_time time) const; ///< Convert absolute time to scheduler time
-                                        /**< This member converst an absolute time value into the
-                                             corresponding scheduler time.
-                                             \see abstime */
-
-        sched_time now() const;         ///< Return current date/time
-                                        /**< The return value represents the current date/time in
-                                             scheduler time representation */
-
-        sched_time eventTime() const;   ///< Return date/time of last event
+        ClockService::clock_type eventTime() const; ///< Return date/time of last event
 
     protected:
 
@@ -226,13 +196,13 @@ namespace senf {
         struct TimerSpec
         {
             TimerSpec() : timeout(), cb() {}
-            TimerSpec(sched_time timeout_, TimerCallback cb_)
+            TimerSpec(ClockService::clock_type timeout_, TimerCallback cb_)
                 : timeout(timeout_), cb(cb_) {}
 
             bool operator< (TimerSpec const & other) const
                 { return timeout > other.timeout; }
 
-            sched_time timeout;
+            ClockService::clock_type timeout;
             TimerCallback cb;
         };
 
@@ -243,7 +213,6 @@ namespace senf {
         TimerQueue timerQueue_;
         int epollFd_;
         bool terminate_;
-        abs_time epoch_;
     };
 
     /** \brief Default file descriptor accessor
index 2521b4a..b87b508 100644 (file)
@@ -184,7 +184,7 @@ namespace {
         callback(handle.fd_,event);
     }
 
-    bool is_close(Scheduler::sched_time a, Scheduler::sched_time b)
+    bool is_close(ClockService::clock_type a, ClockService::clock_type b)
     {
         return (a<b ? b-a : a-b) < 10100000UL; // a little bit over 10ms
     }
@@ -225,11 +225,11 @@ BOOST_AUTO_UNIT_TEST(scheduler)
 
     BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(100000000UL,&timeout) );
     BOOST_CHECK_NO_THROW( Scheduler::instance().timeout(200000000UL,&timeout) );
-    Scheduler::sched_time t (Scheduler::instance().now());
+    ClockService::clock_type t (ClockService::now());
     BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
-    BOOST_CHECK_PREDICATE( is_close, (Scheduler::instance().now()) (t+100000000UL) );
+    BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (t+100000000UL) );
     BOOST_CHECK_NO_THROW( Scheduler::instance().process() );
-    BOOST_CHECK_PREDICATE( is_close, (Scheduler::instance().now()) (t+200000000UL) );
+    BOOST_CHECK_PREDICATE( is_close, (ClockService::now()) (t+200000000UL) );
 
     HandleWrapper handle(sock,"TheTag");
     BOOST_CHECK_NO_THROW( Scheduler::instance().add(handle,&handleCallback,Scheduler::EV_WRITE) );