What if the system time changes while I'm doing timed_wait with a duration?
Asked Answered
T

4

8

When using timed_wait on a boost::condition_variable with a duration, will the wait condition time out after the duration even if the user (or ntp) changes the system time?

E.g.,

boost::posix_time::time_duration wait_duration(0, 0, 1, 0);  // 1 sec
// ** System time jumps back 15 minutes here. **
if( !signal.timed_wait(lock, wait_duration) )
{
    // Does this condition happen 1 second later, or about 15 minutes later?
}
Teleplay answered 7/12, 2010 at 20:10 Comment(1)
@Roddy: OK, but for the record your answer matched my experimental results at the time.Teleplay
B
8

As of the date of writing (Nov 2013), if the wall-clock time changes while you're waiting on a boost condition variable, you simply will get bad results.

If you don't have to use boost, you can use what is called the "monotonic clock." Since the monotonic clock is unaffected by wall-clock time changes, it is not vulnerable to the problem you described. You can safely wait 5 seconds using the pthreads API using something like this:

pthread_condattr_t attr;
pthread_cond_t cond;
struct timespec ts;

pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += 5;
pthreead_cond_timedwait(&cond, &mutex, &ts);

You can check the implementation of boost::condition_variable. Maybe they will fix this some day. The implementation is here: http://svn.boost.org/svn/boost/trunk/boost/thread/pthread/condition_variable.hpp

Bony answered 27/11, 2013 at 23:42 Comment(0)
M
2

I believe it is a race condition, although a very rare one. The implementation of condition_variable::timed_wait() with a duration simply converts the value to a system_time using get_system_time()+wait_duration. If the system time changes between the time get_system_time() is called, and the calculated wait end time is reconverted to a tick-based counter for the underlying OS call, your wait time will be wrong.

To test this idea, on Windows, I wrote a simple program with one thread generating some output every 100ms, like this:

for (;;)
{
    boost::this_thread::sleep( boost::get_system_time() +
        boost::posix_time::milliseconds( 100 ) );
    std::cout << "Ping!" << std::endl;
}

Another thread was setting the system time back one minute in the past every 100ms (this thread uses the OS-level "Sleep()" call which avoids conversions to system time):

for ( ;; )
{
    Sleep( 100 );
    SYSTEMTIME sysTime;
    GetSystemTime( &sysTime );
    FILETIME fileTime;
    SystemTimeToFileTime( &sysTime, /*out*/&fileTime );
    ULARGE_INTEGER fileTime64 = (ULARGE_INTEGER(fileTime.dwHighDateTime) << 32) |
        fileTime.dwLowDateTime;
    fileTime64 -= 10000000 * 60;   // one minute in the past
    fileTime.dwHighDateTime = (fileTime64>>32) & 0xFFFFFFFF;
    fileTime.dwLowDateTime = fileTime64 & 0xFFFFFFFF;
    FileTimeToSystemTime( &fileTime, /*out*/&sysTime );
    SetSystemTime( &sysTime );
}

The first thread, though supposed to output "Ping!" every 100 milliseconds, locked up rather quickly.

Unless I'm missing something, it seems Boost doesn't provide any APIs that avoid this problem of internal conversions to system time, leaving apps vulnerable to outside changes to the clock.

Mum answered 24/10, 2013 at 19:10 Comment(0)
P
1

I did see some problems with this, if your process also uses signals. I also use the Boost condition variables with a duration time.

We have a process that uses a POSIX timer to get accurate timing at 20 Hz. When this timer is activated and the time is set to an earlier date/time the condition variable blocks. When I change the time back to the original value the condition variable continues.

I copied the implementation from Boost and set the clock mode to CLOCK_MONOTONIC. Now the condition variable works correctly even if the time is changed.

It would have been helpful if there would have been a possibility to set the mode of a condition variable to monotonic, but that is not possible at this moment.

Problem answered 9/1, 2012 at 14:26 Comment(0)
M
1

issue was fixed at 1.61 develop branch:

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

Myer answered 1/8, 2016 at 9:34 Comment(4)
Fix was released in version 1.60 of boost: boost.org/users/history/version_1_60_0.htmlDeceit
@Deceit Can you please tell me whether this fix works for windows OS? If Yes, will this work by default or do I need to call some APIs first? If No, whar are my options?Carrara
@swamy Not sure if the issue affected all platforms or not. Only was reported on Linux (I suspect it affects all). Why not try updating your boost version to see if it fixes it? No need to call any APIs first.Deceit
@Deceit Thanks for the reply. I was able to check in v1.62 on windows machine. I'm using wait_until API with steady_clock and when time is changed forward/backward, the amount of time waited is same. for ex: if I'm waiting till 12:00:30 and move clock forward by 5sec, wait will expire at 12:00:35 instead. Same applies for backward time change.Carrara

© 2022 - 2024 — McMap. All rights reserved.