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