Hi All, Attached is an updated copy of the windows (& framebuffer) scheduling code that has a few benefits over the existing implementation. It fixes the bug where (callback_time == current_time) was not executed immediately, and also simplifies and improves the efficiency of schedule_run() by keeping the callbacks ordered (we now have a maximum of 1 negative test per call rather than requiring the entire list to be iterated, multiple times for outstanding callbacks, on each call.)
Code & simple test-case attached. Comments? R.
/* * Copyright 2008 Vincent Sanders <[email protected]> * Copyright 2011 Richard Wilson <[email protected]> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <sys/time.h> #include <time.h> #include "desktop/browser.h" #include "windows/schedule.h" #include "utils/utils.h" #include "utils/log.h" /* linked list of scheduled callbacks */ static struct nscallback *schedule_list = NULL; /** * scheduled callback. */ struct nscallback { struct nscallback *next; struct timeval tv; void (*callback)(void *p); void *p; }; /** * Schedule a callback. * * \param cs_ival interval before the callback should be made / cs * \param callback callback function * \param p user parameter, passed to callback function * * The callback function will be called as soon as possible after t cs have * passed. */ void schedule(int cs_ival, void (*callback)(void *p), void *p) { struct nscallback *nscb; struct nscallback *cur_nscb; struct timeval tv; tv.tv_sec = cs_ival / 100; /* cs to seconds */ tv.tv_usec = (cs_ival % 100) * 10000; /* remainder to microseconds */ nscb = calloc(1, sizeof(struct nscallback)); LOG(("adding callback %p for %p(%p) at %d cs", nscb, callback, p, cs_ival)); gettimeofday(&nscb->tv, NULL); timeradd(&nscb->tv, &tv, &nscb->tv); nscb->callback = callback; nscb->p = p; /* should we link at the head of the list? */ if ((schedule_list == NULL) || (timercmp(&schedule_list->tv, &nscb->tv, >=))) { nscb->next = schedule_list; schedule_list = nscb; return; } /* find the element we should link in after */ cur_nscb = schedule_list; while ((cur_nscb->next != NULL) && (timercmp(&cur_nscb->next->tv, &nscb->tv, <))) cur_nscb = cur_nscb->next; nscb->next = cur_nscb->next; cur_nscb->next = nscb; } /** * Unschedule a callback. * * \param callback callback function * \param p user parameter, passed to callback function * * All scheduled callbacks matching both callback and p are removed. */ void schedule_remove(void (*callback)(void *p), void *p) { struct nscallback *cur_nscb; struct nscallback *prev_nscb; struct nscallback *unlnk_nscb; if (schedule_list == NULL) return; LOG(("removing %p, %p", callback, p)); cur_nscb = schedule_list; prev_nscb = NULL; while (cur_nscb != NULL) { if ((cur_nscb->callback == callback) && (cur_nscb->p == p)) { /* item to remove */ LOG(("callback entry %p removing %p(%p)", cur_nscb, cur_nscb->callback, cur_nscb->p)); /* remove callback */ unlnk_nscb = cur_nscb; cur_nscb = unlnk_nscb->next; if (prev_nscb == NULL) { schedule_list = cur_nscb; } else { prev_nscb->next = cur_nscb; } free(unlnk_nscb); } else { /* move to next element */ prev_nscb = cur_nscb; cur_nscb = prev_nscb->next; } } } /** * Process scheduled callbacks up to current time. * * @return The number of milliseconds untill the next scheduled event * or -1 for no event. */ int schedule_run(void) { struct timeval tv; struct timeval rettime; struct nscallback *unlnk_nscb; if (schedule_list == NULL) return -1; gettimeofday(&tv, NULL); while ((schedule_list != NULL) && (timercmp(&tv, &schedule_list->tv, >=))) { /* remove callback */ unlnk_nscb = schedule_list; schedule_list = schedule_list->next; LOG(("callback entry %p running %p(%p)", unlnk_nscb, unlnk_nscb->callback, unlnk_nscb->p)); /* call callback */ unlnk_nscb->callback(unlnk_nscb->p); free(unlnk_nscb); } if (schedule_list == NULL) return -1; /* make rettime relative to now */ timersub(&schedule_list->tv, &tv, &rettime); /*LOG(("returning time to next event as %ldms", (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000))); */ /* return next event time in milliseconds (24days max wait) */ return (rettime.tv_sec * 1000) + (rettime.tv_usec / 1000); } /** * Output a list of the current scheduled callbacks. */ void list_schedule(void) { struct timeval tv; struct nscallback *cur_nscb; gettimeofday(&tv, NULL); LOG(("schedule list at %ld:%ld", tv.tv_sec, tv.tv_usec)); cur_nscb = schedule_list; while (cur_nscb != NULL) { LOG(("Schedule %p at %ld:%ld", cur_nscb, cur_nscb->tv.tv_sec, cur_nscb->tv.tv_usec)); cur_nscb = cur_nscb->next; } } /* * Local Variables: * c-basic-offset:8 * End: */
/* * Copyright 2011 Richard Wilson <[email protected]> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include "desktop/browser.h" #include "windows/schedule.h" #include "utils/log.h" #include "utils/utils.h" extern bool verbose_log = true; void test_schedule(void *v); int main(void) { schedule(300, test_schedule, (void *)1); schedule(200, test_schedule, (void *)2); schedule(100, test_schedule, (void *)3); schedule(400, test_schedule, (void *)4); schedule(250, test_schedule, (void *)5); schedule(50, test_schedule, (void *)6); list_schedule(); while (schedule_run() != -1); return 0; } void test_schedule(void *p) { }
