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