Consideremos el siguiente código, que es totalmente compatible con POSIX:¿Cómo hacer pthread_cond_timedwait() robusto contra las manipulaciones del reloj del sistema?
#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;
}
imprime la fecha actual del sistema cada 5 segundos, sin embargo, hace un sueño de 3 segundos entre obtener la hora actual del sistema (gettimeofday
) y la condición espera (pthread_cond_timedwait
).
Justo después de imprimir "dormir (...)", intente configurar el reloj del sistema dos días pasados. ¿Lo que pasa? Bueno, en lugar de esperar 2 segundos más en la condición que normalmente lo hace, pthread_cond_timedwait
ahora espera dos días y 2 segundos.
¿Cómo puedo solucionar eso?
¿Cómo puedo escribir el código que cumple con POSIX, que no se rompe cuando el usuario manipula el reloj del sistema?
Tenga en cuenta que el reloj del sistema podría cambiar incluso sin interacción del usuario (por ejemplo, un cliente NTP podría actualizar el reloj automáticamente una vez al día). Establecer el reloj en el futuro no es un problema, solo hará que el sueño se despierte temprano, lo que generalmente no es un problema y que puede "detectar" y manejar fácilmente en consecuencia, pero dejando el reloj en el pasado (por ejemplo, porque era funcionando en el futuro, NTP detectó eso y lo solucionó) puede causar un gran problema.
PS:
Ni pthread_condattr_setclock()
ni CLOCK_MONOTONIC
existe en mi sistema. Esos son obligatorios para la especificación POSIX 2008 (parte de "Base"), pero la mayoría de los sistemas todavía siguen la especificación POSIX 2004 a día de hoy y en la especificación POSIX 2004 estos dos eran opcionales (Extensión en tiempo real avanzada).
Estoy tan enojado que esta función toma una estructura 'timespec' y usa tiempo absoluto. Creo que voy a utilizar un enfoque 'pipe()'/'select()' para evitarlo. – mpontillo