Scheduler: BUGFIX: Implement timeoutEarly() / timeoutAdjust()
[senf.git] / Scheduler / ClockService.hh
1 // $Id$
2 //
3 // Copyright (C) 2007
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief ClockService public header */
25
26 #ifndef HH_ClockService_
27 #define HH_ClockService_ 1
28
29 // Custom includes
30 #include <sys/time.h> 
31 #include <boost/utility.hpp>
32 #include <boost/date_time/posix_time/posix_time.hpp>
33 #include <boost/scoped_ptr.hpp>
34 #include <boost/cstdint.hpp>
35 #include "../Utils/singleton.hh"
36
37 //#include "ClockService.mpp"
38 ///////////////////////////////hh.p////////////////////////////////////////
39
40 namespace senf {
41
42 #ifndef DOXYGEN
43     namespace detail { class ClockServiceTest; }
44 #endif
45
46     // Implementation note:
47     //
48     // The clock value is represented as a 64bit unsigned integer number of nanosecods elapsed since
49     // the construction of the ClockService object. 
50     // 
51     // The implementation must provide two features:
52     // a) It must reliably detect clock changes
53     // b) In case of a clock change a reasonably accurate fallback clock value must be provided
54     //
55     // We do this using setitimer/getitimer. We setup an interval timer sending SIGALRM whenever
56     // CheckInverval seconds have elapsed.
57     //
58     // On every SIGALRM signal we save the current value of gettimeofday(). If this new value is
59     // substantially different from the currently saved value + CheckInterval, the clock has been
60     // changed.
61     //
62     // Whenever the current clock value is requested using now(), the current gettimeofday() value
63     // is compared with the saved value. If the difference is substantially more than CheckInterval,
64     // the clock has been changed.
65     //
66     // This provides clock skew detection. If clock skew is detected, we need to move base_ by the
67     // amount the time has been changed. To do this we need an as accurate as possible approximation
68     // of the expected current time value. We need to differentiate two cases:
69     //
70     // a) Clock skew detected within now()
71     //
72     // In this case, we use getitimer() to find the time remaining in the timer. Using this value
73     // and the saved gettimeofday() value we can adjust base_ accordingly.
74     //
75     // b) Clock skew detected in the signal handler
76     //
77     // In this case we use the saved gettimeofday() value + CheckInterval to adjust base_.
78     
79     /** \brief Reliable high precision monotonous clock source
80
81         The ClockService provides a highly accurate monotonous clock source based on
82         gettimeofday(). However, it takes additional precautions to detect clock skew.
83
84         \implementation We use a mix of static and non-static members to achieve high performance
85             in the normal case (no clock skew) and still encapsulate the dependency on legacy C
86             headers. Using the senf::singleton mixin ensures, that the instance is constructed
87             before main even when instance() is not called.
88
89         \bug There is a deadlock condition between ClockService and the streaming of Boost.DateTime
90             values: Boost.DateTime seems to call tzset() whenever writing a date/time value (ugh)
91             and since tzset changes basic date/time values, it seems to block gettimeofday() which
92             leads to the SIGLARM handler blocking indefinitely. Resolution either a) find out, why
93             tzset() of all functions is called or b) move the ClockService heartbeat functionality
94             into the Scheduler.
95       */
96     class ClockService
97         : singleton<ClockService>
98     {
99     public:
100         ///////////////////////////////////////////////////////////////////////////
101         // Types
102
103         /** \brief ClockService timer data type
104             
105             Unsigned integer type representing scheduler time. Scheduler time is measured in
106             nanoseconds relative to some implementation defined reference time.
107          */
108         typedef boost::int_fast64_t clock_type;
109         typedef boost::int_fast64_t int64_type;
110
111         /** \brief Absolute time data type
112
113             Boost.DateTime datatype used to represent absolute date/time values.
114          */
115         typedef boost::posix_time::ptime abstime_type;
116
117         static unsigned const CheckInterval = 10;
118
119         ///////////////////////////////////////////////////////////////////////////
120         ///\name Structors and default members
121         ///@{
122
123         ~ClockService();
124
125         ///@}
126         ///////////////////////////////////////////////////////////////////////////
127
128         static clock_type now();  ///< Return current clock value
129         
130         static abstime_type abstime(clock_type clock); ///< Convert clock to absolute time
131                                         /**< This member converts a clock value into an absolute
132                                              Boost.DateTime value.
133                                              \note You should not base timeout calculations on this
134                                                  absolute time value. Clock time is guaranteed to be
135                                                  monotonous, absolute time may be non-monotonous if
136                                                  the system date/time is changed. */
137
138         static clock_type clock(abstime_type time); ///< Convert absolute time to clock value
139                                         /**< This member converst an absolute time value into the
140                                              corresponding clock value.
141                                              \see abstime */
142
143         static clock_type from_time_t(time_t const & time); 
144                                         ///< Convert legacy time_t to clock value
145                                         /**< This member converts an absolute time value 
146                                              represented as a time_t value into a clock value */
147
148         static clock_type from_timeval(timeval const & time); 
149                                         ///< Convert legacy timeval to clock value
150                                         /**< This member converts an absolute time value
151                                              represented as a timeval value into a clock value */
152
153         static clock_type nanoseconds(int64_type v); ///< Convert \a v nanoseconds to clock_type
154         static clock_type microseconds(int64_type v); ///< Convert \a v microseconds to clock_type
155         static clock_type milliseconds(int64_type v); ///< Convert \a v milliseconds to clock_type
156         static clock_type seconds(int64_type v); ///< Convert \a v seconds to clock_type
157         static clock_type minutes(int64_type v); ///< Convert \a v minutes to clock_type
158         static clock_type hours(int64_type v); ///< Convert \a v hours to clock_type
159         static clock_type days(int64_type v); ///< Convert \a v days to clock_type
160
161         static int64_type in_nanoseconds(clock_type v); ///< Convert \a v to nanoseconds
162         static int64_type in_microseconds(clock_type v); ///< Convert \a v to microseconds
163         static int64_type in_milliseconds(clock_type v); ///< Convert \a v to milliseconds
164         static int64_type in_seconds(clock_type v); ///< Convert \a v to seconds
165         static int64_type in_minutes(clock_type v); ///< Convert \a v to minutes
166         static int64_type in_hours(clock_type v); ///< Convert \a v to hours
167         static int64_type in_days(clock_type v); ///< Convert \a v to days
168
169         static void restart();
170
171     private:
172         ClockService();
173
174         void timer();
175
176         clock_type now_m();
177         abstime_type abstime_m(clock_type clock);
178         clock_type clock_m(abstime_type time);
179         void restart_m(bool restart = true);
180
181         bool checkSkew(boost::posix_time::ptime time);
182         void updateSkew(boost::posix_time::ptime time);
183         void clockSkew(boost::posix_time::ptime time, boost::posix_time::ptime expected);
184
185         void restartTimer(bool restart = true);
186
187         boost::posix_time::ptime base_;
188         boost::posix_time::ptime heartbeat_;
189
190         // I don't want this header to depend on the legacy C headers.
191         /// Internal: ClockService private data (PIMPL idiom)
192         struct Impl;
193         boost::scoped_ptr<Impl> impl_;
194
195         friend class Impl;
196 #ifndef DOXYGEN
197         friend class senf::detail::ClockServiceTest;
198         friend class singleton<ClockService>;
199 #endif
200     };
201
202 }
203
204 ///////////////////////////////hh.e////////////////////////////////////////
205 #include "ClockService.cci"
206 //#include "ClockService.ct"
207 //#include "ClockService.cti"
208 #endif
209
210 \f
211 // Local Variables:
212 // mode: c++
213 // fill-column: 100
214 // comment-column: 40
215 // c-file-style: "senf"
216 // indent-tabs-mode: nil
217 // ispell-local-dictionary: "american"
218 // compile-command: "scons -u test"
219 // End: