1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
/*
* Event manager by time stamp (usec)
*
* FIXME-Performance: reduce calls to gettimeofday(). Use global.
*/
#include "gs-common.h"
#include <gsocket/gsocket.h>
#include "gs-externs.h"
int
GS_EVENT_MGR_init(GS_EVENT_MGR *mgr)
{
memset(mgr, 0, sizeof *mgr);
GS_LIST_init(&mgr->list_ts, 0);
return 0;
}
/*
* func == NULL is a special function which sets the mgr->is_return_to_caller := 1.
* This is used to pass control back to the caller when select() is used
* in any kind of 'forever' loop such as GS_select().
*/
GS_EVENT *
GS_EVENT_add_by_ts(GS_EVENT_MGR *mgr, GS_EVENT *gse, uint64_t start, uint64_t interval, gsevent_cb_t func, void *data, size_t len)
{
if (gse == NULL)
{
gse = calloc(1, sizeof *gse);
XASSERT(gse != NULL, "calloc(): %s\n", strerror(errno));
gse->is_calloc = 1;
} else {
gse->is_calloc = 0;
}
// Get start time if not specified
// Start can also be an offset to current time if it is
// <1000.
if (start < 1000)
{
struct timeval tv;
gettimeofday(&tv, NULL);
start = GS_TV_TO_USEC(&tv) + GS_MSEC_TO_USEC(start);
}
gse->data = data;
gse->len = len;
gse->mgr = mgr;
gse->interval = interval;
gse->start = start;
gse->due = start + interval;
gse->func = func;
gse->id = mgr->id_counter;
mgr->id_counter += 1;
GS_LIST_add(&mgr->list_ts, &gse->li, gse, gse->due);
return gse;
}
int
GS_EVENT_del(GS_EVENT *gse)
{
int is_calloc;
if (gse == NULL)
return -1;
// Already deleted
if (gse->mgr == NULL)
return -1;
GS_LIST_del(&gse->li);
is_calloc = gse->is_calloc;
memset(gse, 0, sizeof *gse);
if (is_calloc)
XFREE(gse);
return 0;
}
uint64_t
GS_EVENT_usec_until_event(GS_EVENT_MGR *mgr)
{
GS_LIST_ITEM *li;
li = GS_LIST_next(&mgr->list_ts, NULL);
// Return 1 second if no event scheduled.
if (li == NULL)
return GS_SEC_TO_USEC(1);
struct timeval tv;
uint64_t now;
gettimeofday(&tv, NULL);
now = GS_TV_TO_USEC(&tv);
// Return if top most entry's time has come...
if (now > li->id)
return 0;
return li->id - now;
}
/*
* Execute 1 event (if due) and return to the caller.
*
* Return 0 if there are more events to be executed.
* Return the usec until next event is due.
*/
uint64_t
GS_EVENT_execute(GS_EVENT_MGR *mgr)
{
uint64_t wait;
int ret;
wait = GS_EVENT_usec_until_event(mgr);
if (wait != 0)
return wait;
// HERE: top-most event is due. Execute.
GS_EVENT *event = mgr->list_ts.head->data;
if (event->func != NULL)
{
ret = event->func(event);
if (ret != 0)
{
// CB wants this event to be deleted
if (ret != -2)
GS_EVENT_del(event);
return 0;
}
} else {
mgr->is_return_to_caller = 1;
}
// Schedule next execution for this event
// Detect clock skew (e.g. machine was in sleep mode)
struct timeval tv;
gettimeofday(&tv, NULL);
uint64_t now = GS_TV_TO_USEC(&tv);
uint64_t steps = (now - event->start) / event->interval;
event->due = event->start + ((steps + 1) * event->interval);
// DEBUGF("now %llu due %llu diff %llu\n", now, event->due, event->due - now);
GS_LIST_relink(&event->li, event->due);
return GS_EVENT_usec_until_event(mgr);
}
/*
* Execute all events that are due.
* Return the usec until next event is due (or 1 sec if no event scheduled)
*/
uint64_t
GS_EVENT_execute_all(GS_EVENT_MGR *mgr)
{
uint64_t next;
while (1)
{
next = GS_EVENT_execute(mgr);
if (next != 0)
break;
}
return next;
}
|