Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?
}
like image 565
indiv Avatar asked Dec 07 '10 20:12

indiv


3 Answers

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

like image 140
cmccabe Avatar answered Oct 16 '22 16:10

cmccabe


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.

like image 2
Charles Savoie Avatar answered Oct 16 '22 18:10

Charles Savoie


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.

like image 1
Borkhuis Avatar answered Oct 16 '22 17:10

Borkhuis