开发者

How to make pthread_cond_timedwait() robust against system clock manipulations?

Consider the following source code, which is fully POSIX compliant:

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>

int main (int argc, char ** argv) {
    pthread_cond_t c;
    pthread_mutex_t m;
    char printTime[UCHAR_MAX];

    pthread_mutex_init(&m, NULL);
    pthread_cond_init(&c, NULL);

    for (;;) {
        struct tm * tm;
        struct timeval tv;
        struct timespec ts;

        gettimeofday(&tv, NULL);

        printf("sleep (%ld)\n", (long)tv.tv_sec);
        sleep(3);

        tm = gmtime(&tv.tv_sec);
        strftime(p开发者_开发问答rintTime, UCHAR_MAX, "%Y-%m-%d %H:%M:%S", tm);
        printf("%s (%ld)\n", printTime, (long)tv.tv_sec);

        ts.tv_sec = tv.tv_sec + 5;
        ts.tv_nsec = tv.tv_usec * 1000;

        pthread_mutex_lock(&m);
        pthread_cond_timedwait(&c, &m, &ts);
        pthread_mutex_unlock(&m);
    }
    return 0;
}

Prints the current system date every 5 seconds, however, it does a sleep of 3 seconds between getting the current system time (gettimeofday) and the condition wait (pthread_cond_timedwait).

Right after it is printing "sleep (...)", try setting the system clock two days into the past. What happens? Well, instead of waiting 2 more seconds on the condition as it usually does, pthread_cond_timedwait now waits for two days and 2 seconds.

How do I fix that?

How can I write POSIX compliant code, that does not break when the user manipulates the system clock?

Please keep in mind that the system clock might change even without user interaction (e.g. a NTP client might update the clock automatically once a day). Setting the clock into the future is no problem, it will only cause the sleep to wake up early, which is usually no problem and which you can easily "detect" and handle accordingly, but setting the clock into the past (e.g. because it was running in the future, NTP detected that and fixed it) can cause a big problem.

PS:

Neither pthread_condattr_setclock() nor CLOCK_MONOTONIC exists on my system. Those are mandatory for the POSIX 2008 specification (part of "Base") but most systems still only follow the POSIX 2004 specification as of today and in the POSIX 2004 specification these two were optional (Advanced Realtime Extension).


Interesting, I've not encountered that behaviour before but, then again, I'm not in the habit of mucking about with my system time that much :-)

Assuming you're doing that for a valid reason, one possible (though kludgy) solution is to have another thread whose sole purpose is to periodically kick the condition variable to wake up any threads so affected.

In other words, something like:

while (1) {
    sleep (10);
    pthread_cond_signal (&condVar);
}

Your code that's waiting for the condition variable to be kicked should be checking its predicate anyway (to take care of spurious wakeups) so this shouldn't have any real detrimental effect on the functionality.

It's a slight performance hit but once every ten seconds shouldn't be too much of a problem. It's only really meant to take care of the situations where (for whatever reason) your timed wait will be waiting a long time.


Another possibility is to re-engineer your application so that you don't need timed waits at all.

In situations where threads need to be woken for some reason, it's invariably by another thread which is perfectly capable of kicking a condition variable to wake one (or broadcasting to wake the lot of them).

This is very similar to the kicking thread I mentioned above but more as an integral part of your architecture than a bolt-on.


You can defend your code against this problem. One easy way is to have one thread whose sole purpose is to watch the system clock. You keep a global linked list of condition variables, and if the clock watcher thread sees a system clock jump, it broadcasts every condition variable on the list. Then, you simply wrap pthread_cond_init and pthread_cond_destroy with code that adds/removes the condition variable to/from the global linked list. Protect the linked list with a mutex.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜