tor/src/common/scheduler.c
Roger Dingledine 9a928eeb12 Initial revision
svn:r2
2002-06-26 22:45:49 +00:00

260 lines
6 KiB
C

/*
* scheduler.c
* Scheduler
*
* Matej Pfajfar <mp292@cam.ac.uk>
*/
/*
* Changes :
* $Log$
* Revision 1.1 2002/06/26 22:45:50 arma
* Initial revision
*
* Revision 1.3 2002/04/02 10:20:37 badbytes
* Bug fixes.
*
* Revision 1.2 2002/03/28 10:49:07 badbytes
* Renamed get_trigger() to sched_trigger().
*
* Revision 1.1 2002/03/28 10:36:55 badbytes
* A generic scheduler.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include "log.h"
#include "scheduler.h"
/* create a new scheduler */
sched_t *new_sched()
{
sched_t *sched;
sched = (sched_t *)malloc(sizeof(sched_t));
if (!sched)
return NULL;
sched->entries = NULL;
return sched;
}
/* delete a scheduler from memory */
void free_sched(sched_t *sched)
{
sched_entry_t *entry;
if (!sched)
return;
while(sched->entries)
{
entry = (sched_entry_t *)sched->entries->next;
free((void *)sched->entries);
sched->entries = entry;
}
}
/* add a new item to the scheduler */
int add_sched_entry(sched_t *sched, struct timeval last, struct timeval interval)
{
sched_entry_t *new_entry;
sched_entry_t *prev;
sched_entry_t *next;
if (!sched) /* invalid parameters */
return -1;
new_entry = (sched_entry_t *)malloc(sizeof(sched_entry_t));
if (!new_entry)
return -1;
new_entry->last = last;
new_entry->interval = interval;
if (!sched->entries) /* empty list */
{
sched->entries = new_entry;
new_entry->prev = NULL;
new_entry->next = NULL;
}
else /* maintain a priority queue of items */
{
/* find the next largest element in the list */
next = sched->entries;
while(next)
{
if (sched_entry_geq(next->last, next->interval, last, interval))
{
prev = (sched_entry_t *)next->prev;
break;
}
else
{
prev = next;
next = (sched_entry_t *)next->next;
}
}
if (prev)
prev->next = (void *)new_entry;
else
sched->entries = new_entry;
if (next)
next->prev = (void *)new_entry;
new_entry->prev = (void *)prev;
new_entry->next = (void *)next;
}
return 0;
}
int remove_sched_entry(sched_t *sched, struct timeval last, struct timeval interval)
{
sched_entry_t *entry;
if (!sched)
return -1;
if (!sched->entries)
return -1;
entry = sched->entries;
while(entry)
{
if ((entry->last.tv_sec == last.tv_sec) && (entry->last.tv_usec = last.tv_usec) && (entry->interval.tv_sec == interval.tv_sec) && (entry->interval.tv_usec == interval.tv_usec))
{
if (entry->prev)
((sched_entry_t *)(entry->prev))->next = entry->next;
else
sched->entries = (sched_entry_t *)entry->next;
if (entry->next)
((sched_entry_t *)(entry->next))->prev = entry->prev;
free((void *)entry);
break;
}
else
entry = (sched_entry_t *)entry->next;
}
if (entry) /* found and deleted */
return 0;
else /* not found */
return -1;
}
/* update an existing item with new values */
int update_sched_entry(sched_t *sched, struct timeval old_last, struct timeval old_interval, struct timeval new_last, struct timeval new_interval)
{
int retval;
if (!sched)
return -1;
/* remove the old entry first */
retval = remove_sched_entry(sched, old_last, old_interval);
if (!retval)
{
/* add the new one */
retval = add_sched_entry(sched, new_last, new_interval);
}
return retval;
}
/* get the time interval from now until the next time an item needs to be serviced */
int sched_trigger(sched_t *sched, struct timeval **result)
{
int retval;
struct timeval *result_val;
struct timeval now;
struct timeval next;
if (!sched) /* invalid parameters */
return -1;
if (!sched->entries) /* no entries */
{
*result = NULL;
return 0;
}
/* take the minimum element in the queue and calculate its next service time */
next.tv_sec = sched->entries->last.tv_sec + sched->entries->interval.tv_sec;
if (sched->entries->last.tv_usec + sched->entries->interval.tv_usec <= 999999)
next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec;
else
{
next.tv_sec++;
next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec - 1000000;
}
/* get current time */
retval = gettimeofday(&now,NULL);
if (retval == -1)
return -1;
/* allocate memory for the result */
result_val = (struct timeval *)malloc(sizeof(struct timeval));
if (!result_val)
return -1;
/* subtract now from next (return zero if negative) */
if ((next.tv_sec > now.tv_sec) || ((next.tv_sec == now.tv_sec) && (next.tv_usec >= now.tv_usec)))
{
result_val->tv_sec = next.tv_sec - now.tv_sec;
if (next.tv_usec >= now.tv_usec)
result_val->tv_usec = next.tv_usec - now.tv_usec;
else
{
result_val->tv_sec--;
result_val->tv_usec = 1000000 + next.tv_usec - now.tv_usec;
}
}
else /* next service time has already passed, return a timeout of zero */
{
result_val->tv_sec = 0;
result_val->tv_usec = 0;
}
*result = result_val;
return 0;
}
int sched_entry_geq(struct timeval last1, struct timeval interval1, struct timeval last2, struct timeval interval2)
{
struct timeval next1;
struct timeval next2;
/* calculate next service time for entry1 */
next1.tv_sec = last1.tv_sec + interval1.tv_sec;
if (last1.tv_usec + interval1.tv_usec <= 999999)
next1.tv_usec = last1.tv_usec + interval1.tv_usec;
else
{
next1.tv_sec++;
next1.tv_usec = last1.tv_usec + interval1.tv_usec - 1000000;
}
/* calculate next service time for entry2 */
next2.tv_sec = last2.tv_sec + interval2.tv_sec;
if (last2.tv_usec + interval2.tv_usec <= 999999)
next2.tv_usec = last2.tv_usec + interval2.tv_usec;
else
{
next2.tv_sec++;
next2.tv_usec = last2.tv_usec + interval2.tv_usec - 1000000;
}
/* compare */
if ((next1.tv_sec > next2.tv_sec) || ((next1.tv_sec == next2.tv_sec) && (next1.tv_usec >= next2.tv_usec)))
return 1;
else
return 0;
}