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) {
}

Reply via email to