Как сделать pthread_cond_timedwait () устойчивым к манипуляциям с системными часами?

Рассмотрим следующий исходный код, полностью совместимый с POSIX:

#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(printTime, 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;
}

Печатает текущую системную дату каждые 5 секунд, однако между получением текущего системного времени выполняется переход в спящий режим продолжительностью 3 секунды ( gettimeofday ) и условное ожидание ( ​​pthread_cond_timedwait ).

Сразу после вывода «сна (...)» попробуйте установить системные часы на два дня назад. Что случилось? Что ж, вместо того, чтобы ждать еще 2 секунды по условию, как обычно, pthread_cond_timedwait теперь ждет два дня и 2 секунды.

Как мне это исправить?
Как я могу написать POSIX-совместимый код, который не ломается, когда пользователь манипулирует системными часами?

Имейте в виду, что системные часы могут измениться даже без взаимодействия с пользователем (например, клиент NTP может обновлять часы автоматически один раз в день ). Установить часы в будущее не проблема, это только вызовет раннее пробуждение сна, что обычно не является проблемой и что вы можете легко «обнаружить» и обработать соответствующим образом, но установив часы в прошлое (например, потому что это было в будущем, NTP обнаружил и исправил это) может вызвать большую проблему.

PS:
Ни pthread_condattr_setclock () , ни CLOCK_MONOTONIC не существует в моей системе. Они являются обязательными для спецификации POSIX 2008 (часть «Базы»), но большинство систем на сегодняшний день все еще следуют только спецификации POSIX 2004, а в спецификации POSIX 2004 эти два были необязательными (Advanced Realtime Extension).

15
задан Mecki 15 July 2015 в 23:11
поделиться