Opened 13 years ago

Last modified 6 years ago

#3504 reopened Feature Requests

deadline_timer (based on UTC time) is not suitable for communication timeouts

Reported by: Bjarne Laursen <bla@…> Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost 1.41.0 Severity: Problem
Keywords: Cc: pgquiles@…, leon@…

Description

If you use the deadline_timer to make a polling every 10th second, it will be heavely affected if someone adjust the system clock with date command, or if the time is adjusted automaticly by NTP.

Uses as this should use a timer that cannot make jumps like that.

I solved this by making a new time_traits called monotone_time. It uses GetTickCount() for windows and clock_gettime(CLOCK_MONOTONIC, ...) for linux. See attachment.

To use it your timer must be a monotone_timer instead of a deadline_timer. You can use both types of timers in you application.

I hope this can be a part of a future release of boost::asio

Attachments (7)

monotone_timer.hpp (3.1 KB ) - added by Bjarne Laursen <bla@…> 13 years ago.
monotone_timer.hpp
monotone_timer.hpp-PROPER_IFDEF (3.2 KB ) - added by Pau Garcia i Quiles <pgquiles@…> 13 years ago.
Fix #ifdef for Unix platforms
monotone_timer.2.hpp (3.6 KB ) - added by victor 13 years ago.
discard mutex on windows
monotone_timer.hpp-NO_MUTEX (3.3 KB ) - added by anonymous 13 years ago.
Removed need for mutex while maintaining monotonic functionality
monotone_timer_better_rollover.cpp (1.5 KB ) - added by anonymous 12 years ago.
monotone_timer_better_rollover.hpp (2.2 KB ) - added by anonymous 12 years ago.
MonotonicDeadlineTimer.h (2.7 KB ) - added by bwhite@… 11 years ago.
monotonic_deadline_timer using boost::chrono::steady_clock

Download all attachments as: .zip

Change History (27)

by Bjarne Laursen <bla@…>, 13 years ago

Attachment: monotone_timer.hpp added

monotone_timer.hpp

comment:1 by Pau Garcia i Quiles <pgquiles@…>, 13 years ago

Cc: pgquiles@… added
Version: Boost 1.38.0Boost 1.41.0

I can confirm Asio timers are broken if you set the date back/forward. The timer does not need to fire any fast, you can fire the timer just once a day and it will fail if you do this:

  1. Let's say the time now is 2009-11-23 13:45:20
  2. Start timer with deadline in 1 hour. Asio watches the system and will fire the timer at 2009-11-23 14:45:20.
  3. Set date back 1 day: 2009-11-22 13:45:20
  4. Timer never fires

While Bjarne's solution is good, the #ifdef for Linux is not. It should not check for Linux but for platforms where clock_gettime is available, which is done with "#if defined(_POSIX_TIMERS) && ( _POSIX_TIMERS > 0 ) && defined(_POSIX_MONOTONIC_CLOCK)".

I've fixed Bjarne's code and uploaded the attachment.

In addition to that, on Linux with glibc >= 2.11, linking with librt is required. Linux with glibc <= 2.9 does not need linking with librt but it does not do any harm, either. FreeBSD and Mac OS X do not need linking with librt.

by Pau Garcia i Quiles <pgquiles@…>, 13 years ago

Fix #ifdef for Unix platforms

by victor, 13 years ago

Attachment: monotone_timer.2.hpp added

discard mutex on windows

comment:2 by victor, 13 years ago

There is static mutex in function 'now' on windows. If there r lots of connection which own monotone_timer, ther will be execute one by one because this static mutex. I fixed the code and unploaded the attachment

comment:3 by chris_kohlhoff, 13 years ago

Resolution: wontfix
Status: newclosed

I agree with the need for a monotonic timer. However, I consider the provision of a monotonic clock to be out of scope for asio. The date_time library might be better. Alternatively, once c++0x library implementations are available we may use those. Once a monotonic clock is available I will look at adding a monotonic_timer to asio.

comment:4 by anonymous, 13 years ago

Resolution: wontfix
Status: closedreopened

Let me disagree. Timers are implemented in asio, not in date_time. Even if only for coherence (other timers are implemented in asio not in date_time), it makes no sense at all to implement the monotonic timer in date_time instead of asio. Please, reconsider your position.

comment:5 by chris_kohlhoff, 13 years ago

Resolution: wontfix
Status: reopenedclosed

Let me restate:

  • Yes, a monotonic_timer would be a good addition to asio. Asio is the right place for this.
  • A monotonic clock is a prerequisite for a monotonic_timer.
  • The provision of a monotonic clock is out of scope for asio as far as I'm concerned. By clock, I mean the monotonic equivalent of boost::posix_time::microsec_clock::universal_time().

I hope that makes it clear.

in reply to:  2 comment:6 by anonymous, 13 years ago

Replying to victor:

There is static mutex in function 'now' on windows. If there r lots of connection which own monotone_timer, ther will be execute one by one because this static mutex. I fixed the code and unploaded the attachment

this version uses _ftime instead of GetTickCount for windows. The original problem seems to return using any system time function other than GetTickCount ???

by anonymous, 13 years ago

Attachment: monotone_timer.hpp-NO_MUTEX added

Removed need for mutex while maintaining monotonic functionality

comment:7 by anonymous, 13 years ago

I've looked into the QueryPerformanceXXXXX functions and it seems they may not be as accurate as I would like. While the GetTickCount version requires a mutex it may be better to suffer a possible small performance hit in order to maintain accuracy.

If anyone has any up to date knowledge of any issues with the QueryPerformanceXXXXX functions I would appreciate it.

in reply to:  7 comment:8 by simon.reye@…, 13 years ago

Replying to anonymous:

I've looked into the QueryPerformanceXXXXX functions and it seems they may not be as accurate as I would like. While the GetTickCount version requires a mutex it may be better to suffer a possible small performance hit in order to maintain accuracy.

If anyone has any up to date knowledge of any issues with the QueryPerformanceXXXXX functions I would appreciate it.

In the past they have been a problem, especially on multi-core processors but MSDN assures us that QueryPerformanceFrequency will not change once the system has started.

http://msdn.microsoft.com/en-us/library/ms644905%28VS.85%29.aspx

comment:9 by anonymous, 12 years ago

I'm confused as to why this is "wontfix". As it stands, any change in the system time completely screws up any system relying on the deadline timer. If not for the files and ideas presented here I would have been lost.

You cannot use the system time for timers.

comment:10 by kjohnson@…, 12 years ago

I may be wrong, but I think the attached file "monotone_timer.hpp-NO_MUTEX" suffers from really bad roll over issues. Multiplying a rolling monotonic timer by a constant (1000000000/frequency.QuadPart on line 80) doesn't produce a valid monotonic nanosecond count.

I've attached the version I'm trying which keeps times and durations in terms of the performance counter (monotone_timer_better_rollover.cpp/.hpp).

You use it as: TimerImpl m_Timer; m_Timer.expires_from_now(TimerTraits::milliseconds_to_duration(milliseconds_delay));

I agree that this is a really big issue. I was hoping when we started using boost that we would be spending out time writing our application, not spending hours trying to get a basic timer working reliably.

I also think the documentation should be changed. The example provided (e.g. http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timers/tick_count_timer.cpp) is *WRONG* and doesn't work as expected.

by anonymous, 12 years ago

by anonymous, 12 years ago

comment:11 by Niklas Angare <li51ckf02@…>, 11 years ago

I'd like to resubmit this feature request now that boost::chrono::steady_clock is available on the release branch.

in reply to:  10 ; comment:12 by anonymous, 11 years ago

Replying to kjohnson@…:

I may be wrong, but I think the attached file "monotone_timer.hpp-NO_MUTEX" suffers from really bad roll over issues. Multiplying a rolling monotonic timer by a constant (1000000000/frequency.QuadPart on line 80) doesn't produce a valid monotonic nanosecond count.

I've attached the version I'm trying which keeps times and durations in terms of the performance counter (monotone_timer_better_rollover.cpp/.hpp).

You use it as: TimerImpl m_Timer; m_Timer.expires_from_now(TimerTraits::milliseconds_to_duration(milliseconds_delay));

Your attached code uses a header named CheckedInt64.h, could you add this as well please?

comment:13 by Leonid Evdokimov <leon@…>, 11 years ago

Cc: leon@… added

in reply to:  12 comment:14 by kjohnson@…, 11 years ago

It is just a class that performs 64-bit arithmetic with overflow checking. This is internal code that I would require permission to release - honestly I can't be bothered doing this. You can just replace it with normal 64-bit arithmetic if you want.

e.g.

boost::posix_time::time_duration TimerTraits::to_posix_duration(

const TimerTraits::duration_type& d)

{

return boost::posix_time::milliseconds(d.ticks_ * 1000 / performance_freq_);

}

Replying to anonymous:

Replying to kjohnson@…:

I may be wrong, but I think the attached file "monotone_timer.hpp-NO_MUTEX" suffers from really bad roll over issues. Multiplying a rolling monotonic timer by a constant (1000000000/frequency.QuadPart on line 80) doesn't produce a valid monotonic nanosecond count.

I've attached the version I'm trying which keeps times and durations in terms of the performance counter (monotone_timer_better_rollover.cpp/.hpp).

You use it as: TimerImpl m_Timer; m_Timer.expires_from_now(TimerTraits::milliseconds_to_duration(milliseconds_delay));

Your attached code uses a header named CheckedInt64.h, could you add this as well please?

in reply to:  10 ; comment:15 by Aaron_Wright@…, 11 years ago

Replying to kjohnson@…:

I may be wrong, but I think the attached file "monotone_timer.hpp-NO_MUTEX" suffers from really bad roll over issues. Multiplying a rolling monotonic timer by a constant (1000000000/frequency.QuadPart on line 80) doesn't produce a valid monotonic nanosecond count.

I'm investigating an issue I've had where timers have stopped firing. I used an implementation just like "monotone_timer.hpp-NO_MUTEX", and I was wanting to narrow down the cause of my problem.

Would it be possible to get a little more detail about why that equation doesn't "produce a valid monotonic nanosecond count"? I would really appreciate it.

in reply to:  15 comment:16 by kjohnson@…, 11 years ago

Replying to Aaron_Wright@…:

Replying to kjohnson@…:

I may be wrong, but I think the attached file "monotone_timer.hpp-NO_MUTEX" suffers from really bad roll over issues. Multiplying a rolling monotonic timer by a constant (1000000000/frequency.QuadPart on line 80) doesn't produce a valid monotonic nanosecond count.

I'm investigating an issue I've had where timers have stopped firing. I used an implementation just like "monotone_timer.hpp-NO_MUTEX", and I was wanting to narrow down the cause of my problem.

Would it be possible to get a little more detail about why that equation doesn't "produce a valid monotonic nanosecond count"? I would really appreciate it.

I was wrong - it doesn't.

It is always true that:

((count+n)*ns_over_freq) - (count*ns_over_freq) = (n*ns_over_freq)

in the ring of unsigned 64-bit numbers - i.e. monotonic steps in the count always produce monotonic steps in the output - it just wraps around more often.

I was obviously a bit confused and frustrated that day.

I did warn you :)

There were a couple of fixes in 1.45 that you may be interested in.

https://svn.boost.org/trac/boost/ticket/4568

https://svn.boost.org/trac/boost/ticket/4745

I've moved off this now and probably can't help anymore.

by bwhite@…, 11 years ago

Attachment: MonotonicDeadlineTimer.h added

monotonic_deadline_timer using boost::chrono::steady_clock

comment:17 by bwhite@…, 11 years ago

I've attached a header file that I recently implemented in our product that was suffering the same issue described in this thread. It provides a ready-to-use boost::asio::monotonic_deadline_timer class that is powered by the boost::chrono::steady_clock. So far in our testing adjusting the system clock forward or back no longer impacts when the timer fires. When you ask for, say, 10 seconds you always get 10 seconds. Hopefully this or something similar will appear in a future Boost version. Enjoy!

comment:18 by Luc Moreault <lmoreault@…>, 10 years ago

Any new plans to fix this or have a monotonic_dealine_timer officially included in Boost?

Also, I noticed a similar behavior using this_thread::sleep()

comment:19 by Smithe687, 6 years ago

Component: asioxpressive
Milestone: Boost 1.41.0Website 1.X
Resolution: wontfix
Severity: ProblemNot Applicable
Status: closedreopened
Type: Feature RequestsLibrary Submissions
Version: Boost 1.41.0Boost.Build-M3

Hello! Do you use Twitter? I'd like to follow you if that would be okay. I'm undoubtedly enjoying your blog and look forward to new posts. dfcdefcffabedadc

comment:20 by Niklas Angare <li51ckf02@…>, 6 years ago

Component: xpressiveasio
Milestone: Website 1.XTo Be Determined
Severity: Not ApplicableProblem
Type: Library SubmissionsFeature Requests
Version: Boost.Build-M3Boost 1.41.0

Undid vandalism. I would like to take this opportunity to reiterate my request that deadline_timer be based on boost::chrono::steady_clock.

Note: See TracTickets for help on using tickets.