Si consideri il seguente codice sorgente, che è completamente compatibile con POSIX:Come rendere pthread_cond_timedwait() affidabile rispetto alle manipolazioni dell'orologio di 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;
}
stampa la data corrente del sistema ogni 5 secondi, invece, si fa un sonno di 3 secondi tra ottenere l'ora di sistema corrente (gettimeofday
) e la condizione attesa (pthread_cond_timedwait
).
Subito dopo aver stampato "sleep (...)", prova a impostare l'orologio di sistema due giorni fa. Che succede? Bene, invece di aspettare altri 2 secondi come al solito, pthread_cond_timedwait
attende due giorni e 2 secondi.
Come posso risolvere il problema?
Come posso scrivere codice conforme POSIX, che non si interrompe quando l'utente manipola l'orologio di sistema?
Si prega di tenere presente che l'orologio di sistema potrebbe cambiare anche senza l'interazione dell'utente (ad esempio un client NTP potrebbe aggiornare l'orologio automaticamente una volta al giorno). L'impostazione dell'orologio nel futuro non è un problema, causerà solo il risveglio del sonno precoce, che di solito non è un problema e che puoi facilmente "rilevare" e gestire di conseguenza, ma impostare l'orologio nel passato (ad esempio perché era l'esecuzione in futuro, NTP rilevato e risolto) può causare un grosso problema.
PS:
Né pthread_condattr_setclock()
né CLOCK_MONOTONIC
esiste sul mio sistema. Queste sono obbligatorie per le specifiche POSIX 2008 (parte di "Base") ma la maggior parte dei sistemi segue ancora le specifiche POSIX 2004 fino ad oggi e nelle specifiche POSIX 2004 queste due erano facoltative (Advanced Realtime Extension).
Sono così infastidito che questa funzione abbia una struttura 'timespec' e usi il tempo assoluto. Penso che userò un approccio 'pipe()'/'select()' per aggirarlo. – mpontillo