o Use nanosleep(2) to sleep exact amount of time till the next second,

not multiple of 1 second, which results in actual time to drift back
and forth every run within 1 second of the actual action has
been set for.

Suggested by:   Ian Lepore

o Schedule the first run in 1 second after starting up, not on the
boundary of the next minute, which results in the every_second jobs
not being run.
This commit is contained in:
Maxim Sobolev 2012-10-17 00:44:34 +00:00
parent e9bbb44e09
commit 7bd028861b

View File

@ -341,37 +341,73 @@ cron_tick(db)
*/ */
static void static void
cron_sync() { cron_sync() {
#if 0
register struct tm *tm; register struct tm *tm;
#endif
TargetTime = time((time_t*)0); TargetTime = time((time_t*)0) + 1;
#if 0
tm = localtime(&TargetTime); tm = localtime(&TargetTime);
TargetTime += (60 - tm->tm_sec); TargetTime += (60 - tm->tm_sec);
#endif
} }
static int
timeval_subtract(struct timespec *result, struct timeval *x, struct timeval *y)
{
int nsec;
/* Perform the carry for the later subtraction by updating y. */
if (x->tv_usec < y->tv_usec) {
nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
y->tv_usec -= 1000000 * nsec;
y->tv_sec += nsec;
}
if (x->tv_usec - y->tv_usec > 1000000) {
nsec = (x->tv_usec - y->tv_usec) / 1000000;
y->tv_usec += 1000000 * nsec;
y->tv_sec -= nsec;
}
/* tv_nsec is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_nsec = (x->tv_usec - y->tv_usec) * 1000;
/* Return difference in seconds */
return (x->tv_sec - y->tv_sec);
}
static void static void
cron_sleep(db) cron_sleep(db)
cron_db *db; cron_db *db;
{ {
int seconds_to_wait = 0; int seconds_to_wait;
int rval;
struct timeval ctime, ttime;
struct timespec stime, remtime;
/* /*
* Loop until we reach the top of the next minute, sleep when possible. * Loop until we reach the top of the next minute, sleep when possible.
*/ */
for (;;) { for (;;) {
seconds_to_wait = (int) (TargetTime - time((time_t*)0)); gettimeofday(&ctime, NULL);
ttime.tv_sec = TargetTime;
ttime.tv_usec = 0;
timeval_subtract(&stime, &ttime, &ctime);
/* /*
* If the seconds_to_wait value is insane, jump the cron * If the seconds_to_wait value is insane, jump the cron
*/ */
if (seconds_to_wait < -600 || seconds_to_wait > 600) { if (stime.tv_sec < -600 || stime.tv_sec > 600) {
cron_clean(db); cron_clean(db);
cron_sync(); cron_sync();
continue; continue;
} }
seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : stime.tv_sec;
Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
getpid(), (long)TargetTime, seconds_to_wait)) getpid(), (long)TargetTime, seconds_to_wait))
@ -380,13 +416,19 @@ cron_sleep(db)
* to run, break * to run, break
*/ */
if (seconds_to_wait <= 0) if (stime.tv_sec < 0)
break; break;
if (job_runqueue() == 0) { if (job_runqueue() == 0) {
Debug(DSCH, ("[%d] sleeping for %d seconds\n", Debug(DSCH, ("[%d] sleeping for %d seconds\n",
getpid(), seconds_to_wait)) getpid(), seconds_to_wait))
sleep(seconds_to_wait); for (;;) {
rval = nanosleep(&stime, &remtime);
if (rval == 0 || errno != EINTR)
break;
stime.tv_sec = remtime.tv_sec;
stime.tv_nsec = remtime.tv_nsec;
}
} }
} }
} }