ccn_schedule.c

Go to the documentation of this file.
00001 /**
00002  * @file ccn_schedule.c
00003  * @brief Support for scheduling events.
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2009 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #include <stddef.h>
00021 #include <stdlib.h>
00022 #include <limits.h>
00023 #include <string.h>
00024 #include <ccn/schedule.h>
00025 
00026 /**
00027  * We use a heap structure (as in heapsort) to
00028  * keep track of the scheduled events to get O(log n)
00029  * behavior.
00030  */
00031 struct ccn_schedule_heap_item {
00032     intptr_t event_time;
00033     struct ccn_scheduled_event *ev;
00034 };
00035 
00036 struct ccn_schedule {
00037     void *clienth;
00038     const struct ccn_gettime *clock;
00039     struct ccn_schedule_heap_item *heap;
00040     int heap_n;
00041     int heap_limit;
00042     int heap_height; /* this is validated just before use */
00043     int now;         /* internal micros corresponding to lasttime  */
00044     struct ccn_timeval lasttime; /* actual time when we last checked  */
00045     int time_has_passed; /* to prevent too-frequent time syscalls */
00046 };
00047 
00048 /*
00049  * update_epoch: reset sched->now to avoid wrapping
00050  */
00051 static void
00052 update_epoch(struct ccn_schedule *sched)
00053 {
00054     struct ccn_schedule_heap_item *heap;
00055     int n;
00056     int i;
00057     int t = sched->now;
00058     heap = sched->heap;
00059     n = sched->heap_n;
00060     for (i = 0; i < n; i++)
00061         heap[i].event_time -= t;
00062     sched->now = 0;
00063 }
00064 
00065 static void
00066 update_time(struct ccn_schedule *sched)
00067 {
00068     struct ccn_timeval now = { 0 };
00069     int elapsed;
00070     if (sched->time_has_passed < 0)
00071         return; /* For testing with clock stopped */
00072     sched->clock->gettime(sched->clock, &now);
00073     // gettimeofday(&now, 0);
00074     if ((unsigned)(now.s - sched->lasttime.s) >= INT_MAX/4000000) {
00075         /* We have taken a backward or large step - do a repair */
00076         sched->lasttime = now;
00077     }
00078     sched->time_has_passed = 1;
00079     elapsed = now.micros - sched->lasttime.micros +
00080         sched->clock->micros_per_base * (now.s - sched->lasttime.s);
00081     if (elapsed + sched->now < elapsed)
00082         update_epoch(sched);
00083     sched->now += elapsed;
00084     sched->lasttime = now;
00085 }
00086 
00087 struct ccn_schedule *
00088 ccn_schedule_create(void *clienth, const struct ccn_gettime *ccnclock)
00089 {
00090     struct ccn_schedule *sched;
00091     if (ccnclock == NULL)
00092         return(NULL);
00093     sched = calloc(1, sizeof(*sched));
00094     if (sched != NULL) {
00095         sched->clienth = clienth;
00096         sched->clock = ccnclock;
00097         update_time(sched);
00098     }
00099     return(sched);
00100 }
00101 
00102 void
00103 ccn_schedule_destroy(struct ccn_schedule **schedp)
00104 {
00105     struct ccn_schedule *sched;
00106     struct ccn_scheduled_event *ev;
00107     struct ccn_schedule_heap_item *heap;
00108     int n;
00109     int i;
00110     sched = *schedp;
00111     if (sched == NULL)
00112         return;
00113     *schedp = NULL;
00114     heap = sched->heap;
00115     if (heap != NULL) {
00116         n = sched->heap_n;
00117         sched->heap = NULL;
00118         for (i = 0; i < n; i++) {
00119             ev = heap[i].ev;
00120             (ev->action)(sched, sched->clienth, ev, CCN_SCHEDULE_CANCEL);
00121             free(ev);
00122         }
00123         free(heap);
00124     }
00125     free(sched);
00126 }
00127 
00128 const struct ccn_gettime *
00129 ccn_schedule_get_gettime(struct ccn_schedule *schedp) {
00130     return(schedp->clock);
00131 }
00132 
00133 /*
00134  * heap_insert: insert a new item
00135  * n is the total heap size, counting the new item
00136  * h must satisfy (n >> h) == 1
00137  */
00138 static void
00139 heap_insert(struct ccn_schedule_heap_item *heap, int micros,
00140             struct ccn_scheduled_event *ev, int h, int n)
00141 {
00142     int i;
00143     for (i = (n >> h); i < n; i = (n >> --h)) {
00144         if (micros <= heap[i-1].event_time) {
00145             intptr_t d = heap[i-1].event_time;
00146             struct ccn_scheduled_event *e = heap[i-1].ev;
00147             heap[i-1].ev = ev;
00148             heap[i-1].event_time = micros;
00149             micros = d;
00150             ev = e;
00151         }
00152     }
00153     heap[n-1].event_time = micros;
00154     heap[n-1].ev = ev;
00155 }
00156 
00157 /*
00158  * heap_sift: remove topmost element
00159  * n is the total heap size, before removal
00160  */
00161 static void
00162 heap_sift(struct ccn_schedule_heap_item *heap, int n)
00163 {
00164     int i, j;
00165     int micros;
00166     if (n < 1)
00167         return;
00168     micros = heap[n-1].event_time;
00169     for (i = 1, j = 2; j < n; i = j, j = 2 * j) {
00170         if (j + 1 < n && heap[j-1].event_time > heap[j].event_time)
00171             j += 1;
00172         if (micros < heap[j-1].event_time)
00173             break;
00174         heap[i-1] = heap[j-1];
00175     }
00176     heap[i-1] = heap[n-1];
00177     heap[n-1].ev = NULL;
00178     heap[n-1].event_time = 0;
00179 }
00180 
00181 /*
00182  * reschedule_event: schedule an event
00183  * ev is already set up and initialized
00184  */
00185 static struct ccn_scheduled_event *
00186 reschedule_event(
00187     struct ccn_schedule *sched,
00188     int micros,
00189     struct ccn_scheduled_event *ev)
00190 {
00191     int lim;
00192     int n;
00193     int h;
00194     struct ccn_schedule_heap_item *heap;
00195     if (micros + sched->now < micros)
00196         update_epoch(sched);
00197     micros += sched->now;
00198     heap = sched->heap;
00199     n = sched->heap_n + 1;
00200     if (n > sched->heap_limit) {
00201         lim = sched->heap_limit + n;
00202         heap = realloc(sched->heap, lim * sizeof(heap[0]));
00203         if (heap == NULL) return(NULL);
00204         memset(&(heap[sched->heap_limit]), 0, (lim - n) * sizeof(heap[0]));
00205         sched->heap_limit = lim;
00206         sched->heap = heap;
00207     }
00208     sched->heap_n = n;
00209     h = sched->heap_height;
00210     while ((n >> h) > 1)
00211         sched->heap_height = ++h;
00212     while ((n >> h) < 1)
00213         sched->heap_height = --h;
00214     heap_insert(heap, micros, ev, h, n);
00215     return(ev);
00216 }
00217 
00218 /*
00219  * ccn_schedule_event: schedule a new event
00220  */
00221 struct ccn_scheduled_event *
00222 ccn_schedule_event(
00223     struct ccn_schedule *sched,
00224     int micros,
00225     ccn_scheduled_action action,
00226     void *evdata,
00227     intptr_t evint)
00228 {
00229     struct ccn_scheduled_event *ev;
00230     ev = calloc(1, sizeof(*ev));
00231     if (ev == NULL) return(NULL);
00232     ev->action = action;
00233     ev->evdata = evdata;
00234     ev->evint = evint;
00235     update_time(sched);
00236     return(reschedule_event(sched, micros, ev));
00237 }
00238 
00239 /* Use a dummy action in cancelled events */ 
00240 static int
00241 ccn_schedule_cancelled_event(struct ccn_schedule *sched, void *clienth,
00242                              struct ccn_scheduled_event *ev, int flags)
00243 {
00244     return(0);
00245 }
00246 
00247 /**
00248  * Cancel a scheduled event.
00249  *
00250  * Cancels the event (calling action with CCN_SCHEDULE_CANCEL set)
00251  * @returns 0 if OK, or -1 if this is not possible.
00252  */
00253 int
00254 ccn_schedule_cancel(struct ccn_schedule *sched, struct ccn_scheduled_event *ev)
00255 {
00256     int res;
00257     if (ev == NULL)
00258         return(-1);
00259     res = (ev->action)(sched, sched->clienth, ev, CCN_SCHEDULE_CANCEL);
00260     if (res > 0)
00261         abort(); /* Bug in ev->action - bad return value */
00262     ev->action = &ccn_schedule_cancelled_event;
00263     ev->evdata = NULL;
00264     ev->evint = 0;
00265     return(0);
00266 }
00267 
00268 static void
00269 ccn_schedule_run_next(struct ccn_schedule *sched)
00270 {
00271     struct ccn_scheduled_event *ev;
00272     int micros;
00273     int res;
00274     if (sched->heap_n == 0) return;
00275     ev = sched->heap[0].ev;
00276     sched->heap[0].ev = NULL;
00277     micros = sched->heap[0].event_time - sched->now;
00278     heap_sift(sched->heap, sched->heap_n--);
00279     res = (ev->action)(sched, sched->clienth, ev, 0);
00280     if (res <= 0) {
00281         free(ev);
00282         return;
00283     }
00284     /*
00285      * Try to reschedule based on the time the
00286      * event was originally scheduled, but if we have gotten
00287      * way behind, just use the current time.
00288      */
00289     if (micros < -(int)(sched->clock->micros_per_base))
00290         micros = 0;
00291     reschedule_event(sched, micros + res, ev);
00292 }
00293 
00294 /*
00295  * ccn_schedule_run: do any scheduled events
00296  * This executes any scheduled actions whose time has come.
00297  * The return value is the number of micros until the next
00298  * scheduled event, or -1 if there are none.
00299  */
00300 int
00301 ccn_schedule_run(struct ccn_schedule *sched)
00302 {
00303     update_time(sched);
00304     while (sched->heap_n > 0 && sched->heap[0].event_time <= sched->now) {
00305         sched->time_has_passed = 0;
00306         ccn_schedule_run_next(sched);
00307         if (sched->time_has_passed)
00308             update_time(sched);
00309     }
00310     if (sched->heap_n == 0)
00311         return(-1);
00312     return(sched->heap[0].event_time - sched->now);
00313 }
00314 
00315 #ifdef TESTSCHEDULE
00316 // cc -g -o testschedule -DTESTSCHEDULE=main -I../include ccn_schedule.c
00317 #include <stdio.h>
00318 #include <sys/time.h>
00319 
00320 static void
00321 my_gettime(const struct ccn_gettime *self, struct ccn_timeval *result)
00322 {
00323     struct timeval now = {0};
00324     gettimeofday(&now, 0);
00325     result->s = now.tv_sec;
00326     result->micros = now.tv_usec;
00327 }
00328 
00329 static struct ccn_gettime gt = {"getTOD", &my_gettime, 1000000, NULL};
00330 
00331 static void
00332 testtick(struct ccn_schedule *sched)
00333 {
00334     sched->now = sched->heap[0].event_time + 1;
00335     printf("%ld: ", (long)sched->heap[0].event_time);
00336     ccn_schedule_run_next(sched);
00337     printf("\n");
00338 }
00339 static char dd[] = "ABDEFGHI";
00340 #define SARGS struct ccn_schedule *sched, void *clienth, struct ccn_scheduled_event *ev, int flags
00341 static int A(SARGS) { if (flags & CCN_SCHEDULE_CANCEL) return(0);
00342                       printf("A"); return 70000000; }
00343 static int B(SARGS) { printf("B"); return 0; }
00344 static int C(SARGS) { printf("C"); return 0; }
00345 static int D(SARGS) { if (flags & CCN_SCHEDULE_CANCEL) return(0);
00346                       printf("D");  return 30000000; }
00347 static struct ccn_schedule_heap_item tst[7];
00348 int TESTSCHEDULE()
00349 {
00350     struct ccn_schedule *s = ccn_schedule_create(dd+5, &gt);
00351     int i;
00352     struct ccn_scheduled_event *victim = NULL;
00353     // s->heap = tst; s->heap_limit = 7; // uncomment for easy debugger display
00354     s->time_has_passed = -1; /* don't really ask for time */
00355     ccn_schedule_event(s, 11111, A, dd+4, 11111);
00356     ccn_schedule_event(s, 1, A, dd, 1);
00357     ccn_schedule_event(s, 111, C, dd+2, 111);
00358     victim = ccn_schedule_event(s, 1111111, A, dd+6, 1111111);
00359     ccn_schedule_event(s, 11, B, dd+1, 11);
00360     testtick(s);
00361     ccn_schedule_event(s, 1111, D, dd+3, 1111);
00362     ccn_schedule_event(s, 111111, B, dd+5, 111111);
00363     for (i = 0; i < 100; i++) {
00364         if (i == 50) { ccn_schedule_cancel(s, victim); victim = NULL; }
00365         testtick(s);
00366     }
00367     ccn_schedule_destroy(&s);
00368     return(0);
00369 }
00370 #endif

Generated on Thu Feb 16 00:44:00 2012 for Content-Centric Networking in C by  doxygen 1.5.6