Author: sobomax
Date: Thu Oct 25 22:54:29 2012
New Revision: 242101
URL: http://svn.freebsd.org/changeset/base/242101

Log:
  Second attempt to add @every_second keyword support. Due to multiple
  requests, default to the previous 60-seconds scheduling method
  unless there is any @every_second entries to conserve CPU cycles and
  power.
  
  This change also improves scheduling in the default mode by running
  as close to the beginning of the minnute as possible by replacing
  sleep(3) with nanosleep(2). Previously, the tasks would run anywhere
  within the first second of the minute and that offset drifted back
  and forth each time cron(8) was engaged.
  
  MFC after:    1 month

Modified:
  head/usr.sbin/cron/cron/cron.c
  head/usr.sbin/cron/cron/cron.h
  head/usr.sbin/cron/crontab/crontab.5
  head/usr.sbin/cron/lib/entry.c

Modified: head/usr.sbin/cron/cron/cron.c
==============================================================================
--- head/usr.sbin/cron/cron/cron.c      Thu Oct 25 21:22:05 2012        
(r242100)
+++ head/usr.sbin/cron/cron/cron.c      Thu Oct 25 22:54:29 2012        
(r242101)
@@ -35,9 +35,9 @@ static const char rcsid[] =
 
 static void    usage(void),
                run_reboot_jobs(cron_db *),
-               cron_tick(cron_db *),
-               cron_sync(void),
-               cron_sleep(cron_db *),
+               cron_tick(cron_db *, int),
+               cron_sync(int),
+               cron_sleep(cron_db *, int),
                cron_clean(cron_db *),
 #ifdef USE_SIGCHLD
                sigchld_handler(int),
@@ -45,6 +45,8 @@ static        void    usage(void),
                sighup_handler(int),
                parse_args(int c, char *v[]);
 
+static int     run_at_secres(cron_db *);
+
 static time_t  last_time = 0;
 static int     dst_enabled = 0;
 struct pidfh *pfh;
@@ -98,6 +100,9 @@ main(argc, argv)
        char    *argv[];
 {
        cron_db database;
+       int runnum;
+       int secres1, secres2;
+       struct tm *tm;
 
        ProgramName = argv[0];
 
@@ -147,23 +152,47 @@ main(argc, argv)
        database.tail = NULL;
        database.mtime = (time_t) 0;
        load_database(&database);
+       secres1 = secres2 = run_at_secres(&database);
        run_reboot_jobs(&database);
-       cron_sync();
+       cron_sync(secres1);
+       runnum = 0;
        while (TRUE) {
 # if DEBUGGING
            /* if (!(DebugFlags & DTEST)) */
 # endif /*DEBUGGING*/
-                       cron_sleep(&database);
+                       cron_sleep(&database, secres1);
 
-               load_database(&database);
+               if (secres1 == 0 || runnum % 60 == 0) {
+                       load_database(&database);
+                       secres2 = run_at_secres(&database);
+                       if (secres2 != secres1) {
+                               secres1 = secres2;
+                               if (secres1 != 0) {
+                                       runnum = 0;
+                               } else {
+                                       /*
+                                        * Going from 1 sec to 60 sec res. If we
+                                        * are already at minute's boundary, so
+                                        * let it run, otherwise schedule for 
the
+                                        * next minute.
+                                        */
+                                       tm = localtime(&TargetTime);
+                                       if (tm->tm_sec > 0)  {
+                                               cron_sync(secres2);
+                                               continue;
+                                       }
+                               }
+                       }
+               }
 
                /* do this iteration
                 */
-               cron_tick(&database);
+               cron_tick(&database, secres1);
 
-               /* sleep 1 minute
+               /* sleep 1 or 60 seconds
                 */
-               TargetTime += 60;
+               TargetTime += (secres1 != 0) ? 1 : 60;
+               runnum += 1;
        }
 }
 
@@ -187,29 +216,29 @@ run_reboot_jobs(db)
 
 
 static void
-cron_tick(db)
-       cron_db *db;
+cron_tick(cron_db *db, int secres)
 {
        static struct tm        lasttm;
        static time_t   diff = 0, /* time difference in seconds from the last 
offset change */
                difflimit = 0; /* end point for the time zone correction */
        struct tm       otztm; /* time in the old time zone */
-       int             otzminute, otzhour, otzdom, otzmonth, otzdow;
+       int             otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow;
        register struct tm      *tm = localtime(&TargetTime);
-       register int            minute, hour, dom, month, dow;
+       register int            second, minute, hour, dom, month, dow;
        register user           *u;
        register entry          *e;
 
        /* make 0-based values out of these so we can use them as indicies
         */
+       second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND;
        minute = tm->tm_min -FIRST_MINUTE;
        hour = tm->tm_hour -FIRST_HOUR;
        dom = tm->tm_mday -FIRST_DOM;
        month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
        dow = tm->tm_wday -FIRST_DOW;
 
-       Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
-               getpid(), minute, hour, dom, month, dow))
+       Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n",
+               getpid(), second, minute, hour, dom, month, dow))
 
        if (dst_enabled && last_time != 0 
        && TargetTime > last_time /* exclude stepping back */
@@ -262,6 +291,7 @@ cron_tick(db)
 
                        /* make 0-based values out of these so we can use them 
as indicies
                         */
+                       otzsecond = (secres == 0) ? 0 : otztm.tm_sec 
-FIRST_SECOND;
                        otzminute = otztm.tm_min -FIRST_MINUTE;
                        otzhour = otztm.tm_hour -FIRST_HOUR;
                        otzdom = otztm.tm_mday -FIRST_DOM;
@@ -283,7 +313,8 @@ cron_tick(db)
                                          e->uid, e->gid, e->cmd))
 
                        if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
-                               if (bit_test(e->minute, otzminute)
+                               if (bit_test(e->second, otzsecond)
+                                && bit_test(e->minute, otzminute)
                                 && bit_test(e->hour, otzhour)
                                 && bit_test(e->month, otzmonth)
                                 && ( ((e->flags & DOM_STAR) || (e->flags & 
DOW_STAR))
@@ -302,7 +333,8 @@ cron_tick(db)
                                        continue;
                        }
 
-                       if (bit_test(e->minute, minute)
+                       if (bit_test(e->second, second)
+                        && bit_test(e->minute, minute)
                         && bit_test(e->hour, hour)
                         && bit_test(e->month, month)
                         && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
@@ -332,38 +364,74 @@ cron_tick(db)
  * that's something sysadmin's know to expect what with crashing computers..
  */
 static void
-cron_sync() {
-       register struct tm      *tm;
+cron_sync(int secres) {
+       struct tm *tm;
 
        TargetTime = time((time_t*)0);
-       tm = localtime(&TargetTime);
-       TargetTime += (60 - tm->tm_sec);
+       if (secres != 0) {
+               TargetTime += 1;
+       } else {
+               tm = localtime(&TargetTime);
+               TargetTime += (60 - tm->tm_sec);
+       }
 }
 
+static int
+timespec_subtract(struct timespec *result, struct timespec *x,
+    struct timespec *y)
+{
+       time_t nsec;
+
+       /* Perform the carry for the later subtraction by updating y. */
+       if (x->tv_nsec < y->tv_nsec) {
+               nsec = (y->tv_nsec - x->tv_nsec) / 10000000 + 1;
+               y->tv_nsec -= 1000000000 * nsec;
+               y->tv_sec += nsec;
+       }
+       if (x->tv_nsec - y->tv_nsec > 1000000000) {
+               nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
+               y->tv_nsec += 1000000000 * nsec;
+               y->tv_sec -= nsec;
+       }
+     
+       /* tv_nsec is certainly positive. */
+       result->tv_sec = x->tv_sec - y->tv_sec;
+       result->tv_nsec = x->tv_nsec - y->tv_nsec;
+     
+       /* Return True if result is negative. */
+       return (x->tv_sec < y->tv_sec);
+}
 
 static void
-cron_sleep(db)
-       cron_db *db;
+cron_sleep(cron_db *db, int secres)
 {
-       int     seconds_to_wait = 0;
+       int seconds_to_wait;
+       int rval;
+       struct timespec ctime, ttime, stime, remtime;
 
        /*
         * Loop until we reach the top of the next minute, sleep when possible.
         */
 
        for (;;) {
-               seconds_to_wait = (int) (TargetTime - time((time_t*)0));
+               clock_gettime(CLOCK_REALTIME, &ctime);
+               ttime.tv_sec = TargetTime;
+               ttime.tv_nsec = 0;
+               timespec_subtract(&stime, &ttime, &ctime);
 
                /*
                 * 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_sync();
+                       cron_sync(secres);
                        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",
                        getpid(), (long)TargetTime, seconds_to_wait))
 
@@ -372,13 +440,19 @@ cron_sleep(db)
                 * to run, break
                 */
 
-               if (seconds_to_wait <= 0)
+               if (stime.tv_sec < 0)
                        break;
                if (job_runqueue() == 0) {
                        Debug(DSCH, ("[%d] sleeping for %d seconds\n",
                                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;
+                       }
                }
        }
 }
@@ -484,3 +558,17 @@ parse_args(argc, argv)
        }
 }
 
+static int
+run_at_secres(cron_db *db)
+{
+       user *u;
+       entry *e;
+
+       for (u = db->head;  u != NULL;  u = u->next) {
+               for (e = u->crontab;  e != NULL;  e = e->next) {
+                       if ((e->flags & SEC_RES) != 0)
+                               return 1;
+               }
+       }
+       return 0;
+}

Modified: head/usr.sbin/cron/cron/cron.h
==============================================================================
--- head/usr.sbin/cron/cron/cron.h      Thu Oct 25 21:22:05 2012        
(r242100)
+++ head/usr.sbin/cron/cron/cron.h      Thu Oct 25 22:54:29 2012        
(r242101)
@@ -124,6 +124,10 @@
                         LineNumber = ln; \
                        }
 
+#define        FIRST_SECOND    0
+#define        LAST_SECOND     59
+#define        SECOND_COUNT    (LAST_SECOND - FIRST_SECOND + 1)
+
 #define        FIRST_MINUTE    0
 #define        LAST_MINUTE     59
 #define        MINUTE_COUNT    (LAST_MINUTE - FIRST_MINUTE + 1)
@@ -165,6 +169,7 @@ typedef     struct _entry {
 #endif
        char            **envp;
        char            *cmd;
+       bitstr_t        bit_decl(second, SECOND_COUNT);
        bitstr_t        bit_decl(minute, MINUTE_COUNT);
        bitstr_t        bit_decl(hour,   HOUR_COUNT);
        bitstr_t        bit_decl(dom,    DOM_COUNT);
@@ -176,6 +181,7 @@ typedef     struct _entry {
 #define        WHEN_REBOOT     0x04
 #define        RUN_AT  0x08
 #define        NOT_UNTIL       0x10
+#define        SEC_RES         0x20
        time_t  lastrun;
 } entry;
 

Modified: head/usr.sbin/cron/crontab/crontab.5
==============================================================================
--- head/usr.sbin/cron/crontab/crontab.5        Thu Oct 25 21:22:05 2012        
(r242100)
+++ head/usr.sbin/cron/crontab/crontab.5        Thu Oct 25 22:54:29 2012        
(r242101)
@@ -232,6 +232,8 @@ string              meaning
 @daily         Run once a day, "0 0 * * *".
 @midnight      (same as @daily)
 @hourly                Run once an hour, "0 * * * *".
+@every_minute  Run once a minute, "*/1 * * * *".
+@every_second  Run once a second.
 .Ed
 .Sh EXAMPLE CRON FILE
 .Bd -literal

Modified: head/usr.sbin/cron/lib/entry.c
==============================================================================
--- head/usr.sbin/cron/lib/entry.c      Thu Oct 25 21:22:05 2012        
(r242100)
+++ head/usr.sbin/cron/lib/entry.c      Thu Oct 25 22:54:29 2012        
(r242101)
@@ -151,6 +151,7 @@ load_entry(file, error_func, pw, envp)
                        e->flags |= WHEN_REBOOT;
                } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
                        Debug(DPARS, ("load_entry()...yearly shortcut\n"))
+                       bit_set(e->second, 0);
                        bit_set(e->minute, 0);
                        bit_set(e->hour, 0);
                        bit_set(e->dom, 0);
@@ -159,6 +160,7 @@ load_entry(file, error_func, pw, envp)
                        e->flags |= DOW_STAR;
                } else if (!strcmp("monthly", cmd)) {
                        Debug(DPARS, ("load_entry()...monthly shortcut\n"))
+                       bit_set(e->second, 0);
                        bit_set(e->minute, 0);
                        bit_set(e->hour, 0);
                        bit_set(e->dom, 0);
@@ -167,6 +169,7 @@ load_entry(file, error_func, pw, envp)
                        e->flags |= DOW_STAR;
                } else if (!strcmp("weekly", cmd)) {
                        Debug(DPARS, ("load_entry()...weekly shortcut\n"))
+                       bit_set(e->second, 0);
                        bit_set(e->minute, 0);
                        bit_set(e->hour, 0);
                        bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
@@ -175,6 +178,7 @@ load_entry(file, error_func, pw, envp)
                        bit_set(e->dow, 0);
                } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
                        Debug(DPARS, ("load_entry()...daily shortcut\n"))
+                       bit_set(e->second, 0);
                        bit_set(e->minute, 0);
                        bit_set(e->hour, 0);
                        bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
@@ -182,11 +186,29 @@ load_entry(file, error_func, pw, envp)
                        bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
                } else if (!strcmp("hourly", cmd)) {
                        Debug(DPARS, ("load_entry()...hourly shortcut\n"))
+                       bit_set(e->second, 0);
                        bit_set(e->minute, 0);
                        bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
                        bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
                        bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
                        bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+               } else if (!strcmp("every_minute", cmd)) {
+                       Debug(DPARS, ("load_entry()...every_minute shortcut\n"))
+                       bit_set(e->second, 0);
+                       bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
+                       bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+                       bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+                       bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+                       bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+               } else if (!strcmp("every_second", cmd)) {
+                       Debug(DPARS, ("load_entry()...every_second shortcut\n"))
+                       e->flags |= SEC_RES;
+                       bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1));
+                       bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
+                       bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+                       bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+                       bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+                       bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
                } else {
                        ecode = e_timespec;
                        goto eof;
@@ -201,6 +223,7 @@ load_entry(file, error_func, pw, envp)
                }
        } else {
                Debug(DPARS, ("load_entry()...about to parse numerics\n"))
+               bit_set(e->second, 0);
 
                ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
                              PPC_NULL, ch, file);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to