1 /* $Id$ */
2 /* Copyright (c) 2005-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS System libSystem */
4 /* This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, version 3 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
15 
16 
17 
18 #include <assert.h>
19 #ifdef __WIN32__
20 # include <Winsock2.h>
21 typedef int suseconds_t; /* XXX */
22 #else
23 # include <sys/select.h>
24 #endif
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <string.h>
30 #include <limits.h>
31 #ifdef DEBUG
32 # include <stdio.h>
33 #endif
34 #include <errno.h>
35 #include "System/array.h"
36 #include "System/error.h"
37 #include "System/object.h"
38 #include "System/event.h"
39 
40 /* macros */
41 #ifndef max
42 # define max(a, b) ((a) >= (b)) ? (a) : (b)
43 #endif
44 
45 
46 /* Event */
47 /* private */
48 /* types */
49 typedef struct _EventTimeout
50 {
51 	struct timeval initial;
52 	struct timeval timeout;
53 	EventTimeoutFunc func;
54 	void * data;
55 } EventTimeout;
56 ARRAY(EventTimeout *, eventtimeout)
57 
58 typedef struct _EventIO
59 {
60 	int fd;
61 	EventIOFunc func;
62 	void * data;
63 } EventIO;
64 ARRAY(EventIO *, eventio)
65 
66 struct _Event
67 {
68 	unsigned int loop;
69 	int fdmax;
70 	fd_set rfds;
71 	fd_set wfds;
72 	eventioArray * reads;
73 	eventioArray * writes;
74 	eventtimeoutArray * timeouts;
75 	struct timeval timeout;
76 };
77 
78 
79 /* public */
80 /* functions */
81 /* event_new */
event_new(void)82 Event * event_new(void)
83 {
84 	Event * event;
85 
86 	if((event = object_new(sizeof(*event))) == NULL)
87 		return NULL;
88 	event->timeouts = eventtimeoutarray_new();
89 	event->loop = 0;
90 	event->fdmax = -1;
91 	FD_ZERO(&event->rfds);
92 	FD_ZERO(&event->wfds);
93 	event->reads = eventioarray_new();
94 	event->writes = eventioarray_new();
95 	event->timeout.tv_sec = (time_t)LONG_MAX;
96 	event->timeout.tv_usec = (suseconds_t)LONG_MAX;
97 	if(event->timeouts == NULL || event->reads == NULL
98 			|| event->writes == NULL)
99 	{
100 		event_delete(event);
101 		return NULL;
102 	}
103 	return event;
104 }
105 
106 
107 /* event_delete */
event_delete(Event * event)108 void event_delete(Event * event)
109 {
110 	unsigned int i;
111 	EventTimeout * et;
112 	EventIO * eio;
113 
114 	for(i = 0; i < array_count(event->timeouts); i++)
115 	{
116 		array_get_copy(event->timeouts, i, &et);
117 		object_delete(et);
118 	}
119 	array_delete(event->timeouts);
120 	for(i = 0; i < array_count(event->reads); i++)
121 	{
122 		array_get_copy(event->reads, i, &eio);
123 		object_delete(eio);
124 	}
125 	array_delete(event->reads);
126 	for(i = 0; i < array_count(event->writes); i++)
127 	{
128 		array_get_copy(event->writes, i, &eio);
129 		object_delete(eio);
130 	}
131 	array_delete(event->writes);
132 	object_delete(event);
133 }
134 
135 
136 /* useful */
137 /* event_loop */
138 static int _loop_timeout(Event * event);
139 static void _loop_io(Event * event, eventioArray * eios, fd_set * fds);
140 
event_loop(Event * event)141 int event_loop(Event * event)
142 {
143 	struct timeval tv = event->timeout;
144 	struct timeval * timeout = (tv.tv_sec == (time_t)LONG_MAX
145 			&& tv.tv_usec == (suseconds_t)LONG_MAX) ? NULL : &tv;
146 	fd_set rfds = event->rfds;
147 	fd_set wfds = event->wfds;
148 
149 	event->loop++;
150 	while(event->loop && (timeout != NULL || event->fdmax != -1))
151 	{
152 		if(select(event->fdmax + 1, &rfds, &wfds, NULL, timeout) < 0)
153 			return -error_set_code(1, "%s", strerror(errno));
154 		if(_loop_timeout(event) != 0)
155 			return -1;
156 		_loop_io(event, event->reads, &rfds);
157 		_loop_io(event, event->writes, &wfds);
158 		if(event->timeout.tv_sec == (time_t)LONG_MAX
159 				&& event->timeout.tv_usec
160 				== (suseconds_t)LONG_MAX)
161 			timeout = NULL;
162 		else
163 			timeout = &event->timeout;
164 		rfds = event->rfds;
165 		wfds = event->wfds;
166 	}
167 	return 0;
168 }
169 
_loop_timeout(Event * event)170 static int _loop_timeout(Event * event)
171 {
172 	struct timeval now;
173 	unsigned int i = 0;
174 	EventTimeout * et;
175 
176 	if(gettimeofday(&now, NULL) != 0)
177 	{
178 		error_set_code(1, "%s", strerror(errno));
179 		return -1;
180 	}
181 	event->timeout.tv_sec = (time_t)LONG_MAX;
182 	event->timeout.tv_usec = (suseconds_t)LONG_MAX;
183 	while(i < array_count(event->timeouts))
184 	{
185 		array_get_copy(event->timeouts, i, &et);
186 		if(now.tv_sec > et->timeout.tv_sec
187 				|| (now.tv_sec == et->timeout.tv_sec
188 					&& now.tv_usec >= et->timeout.tv_usec))
189 		{
190 			if(et->func(et->data) != 0)
191 			{
192 				array_remove_pos(event->timeouts, i);
193 				object_delete(et);
194 				continue;
195 			}
196 			et->timeout.tv_sec = et->initial.tv_sec + now.tv_sec;
197 			et->timeout.tv_usec = et->initial.tv_usec + now.tv_usec;
198 			if(et->initial.tv_sec < event->timeout.tv_sec
199 					|| (et->initial.tv_sec
200 						== event->timeout.tv_sec
201 						&& et->initial.tv_usec
202 						< event->timeout.tv_usec))
203 			{
204 				event->timeout.tv_sec = et->initial.tv_sec;
205 				event->timeout.tv_usec = et->initial.tv_usec;
206 			}
207 		}
208 		else
209 		{
210 			if(et->timeout.tv_sec - now.tv_sec < event->timeout.tv_sec
211 					|| (et->timeout.tv_sec - now.tv_sec == event->timeout.tv_sec
212 						&& et->timeout.tv_usec - now.tv_usec < event->timeout.tv_usec))
213 			{
214 				event->timeout.tv_sec = et->timeout.tv_sec
215 					- now.tv_sec;
216 				/* FIXME may be needed elsewhere too */
217 				if(et->timeout.tv_usec >= now.tv_usec)
218 					event->timeout.tv_usec
219 						= et->timeout.tv_usec
220 						- now.tv_usec;
221 				else
222 				{
223 					event->timeout.tv_sec--;
224 					event->timeout.tv_usec
225 						= now.tv_usec
226 						- et->timeout.tv_usec;
227 				}
228 			}
229 		}
230 		i++;
231 	}
232 #ifdef DEBUG
233 	fprintf(stderr, "DEBUG: %s() %s%ld%s%ld => 0\n", __func__, "tv_sec=",
234 			(long)event->timeout.tv_sec, ", tv_usec=",
235 			(long)event->timeout.tv_usec);
236 #endif
237 	return 0;
238 }
239 
_loop_io(Event * event,eventioArray * eios,fd_set * fds)240 static void _loop_io(Event * event, eventioArray * eios, fd_set * fds)
241 {
242 	unsigned int i = 0;
243 	EventIO * eio;
244 	int fd;
245 
246 	while(i < array_count(eios))
247 	{
248 		array_get_copy(eios, i, &eio);
249 		if((fd = eio->fd) <= event->fdmax && FD_ISSET(fd, fds)
250 				&& eio->func(fd, eio->data) != 0)
251 		{
252 			if(eios == event->reads)
253 				event_unregister_io_read(event, fd);
254 			else if(eios == event->writes)
255 				event_unregister_io_write(event, fd);
256 #ifdef DEBUG
257 			else
258 				fprintf(stderr, "DEBUG: %s%s", __func__,
259 						"(): should not happen\n");
260 #endif
261 		}
262 		else
263 			i++;
264 	}
265 }
266 
267 
268 /* event_loop_quit */
event_loop_quit(Event * event)269 void event_loop_quit(Event * event)
270 {
271 	if(event->loop > 0)
272 		event->loop--;
273 }
274 
275 
276 /* event_register_idle */
event_register_idle(Event * event,EventTimeoutFunc func,void * data)277 int event_register_idle(Event * event, EventTimeoutFunc func, void * data)
278 {
279 	struct timeval tv;
280 
281 	tv.tv_sec = 0;
282 	tv.tv_usec = 0;
283 	return event_register_timeout(event, &tv, func, data);
284 }
285 
286 
287 /* event_register_io_read */
event_register_io_read(Event * event,int fd,EventIOFunc func,void * userdata)288 int event_register_io_read(Event * event, int fd, EventIOFunc func,
289 		void * userdata)
290 {
291 	EventIO * eventio;
292 
293 	assert(fd >= 0);
294 	if((eventio = object_new(sizeof(*eventio))) == NULL)
295 		return 1;
296 	eventio->fd = fd;
297 	eventio->func = func;
298 	eventio->data = userdata;
299 	event->fdmax = max(event->fdmax, fd);
300 	if(array_append(event->reads, &eventio) != 0)
301 	{
302 		object_delete(eventio);
303 		return -1;
304 	}
305 	FD_SET(fd, &event->rfds);
306 	return 0;
307 }
308 
309 
310 /* event_register_io_write */
event_register_io_write(Event * event,int fd,EventIOFunc func,void * userdata)311 int event_register_io_write(Event * event, int fd, EventIOFunc func,
312 		void * userdata)
313 {
314 	EventIO * eventio;
315 
316 	assert(fd >= 0);
317 	if((eventio = object_new(sizeof(*eventio))) == NULL)
318 		return 1;
319 	eventio->fd = fd;
320 	eventio->func = func;
321 	eventio->data = userdata;
322 	event->fdmax = max(event->fdmax, fd);
323 	if(array_append(event->writes, &eventio) != 0)
324 	{
325 		object_delete(eventio);
326 		return -1;
327 	}
328 	FD_SET(fd, &event->wfds);
329 	return 0;
330 }
331 
332 
333 /* event_register_timeout */
event_register_timeout(Event * event,struct timeval * timeout,EventTimeoutFunc func,void * data)334 int event_register_timeout(Event * event, struct timeval * timeout,
335 		EventTimeoutFunc func, void * data)
336 {
337 	EventTimeout * eventtimeout;
338 	struct timeval now;
339 
340 	if(gettimeofday(&now, NULL) != 0)
341 		return -error_set_code(1, "%s", strerror(errno));
342 	if((eventtimeout = object_new(sizeof(*eventtimeout))) == NULL)
343 		return -1;
344 	eventtimeout->initial.tv_sec = timeout->tv_sec;
345 	eventtimeout->initial.tv_usec = timeout->tv_usec;
346 	eventtimeout->timeout.tv_sec = now.tv_sec + timeout->tv_sec;
347 	eventtimeout->timeout.tv_usec = now.tv_usec + timeout->tv_usec;
348 	eventtimeout->func = func;
349 	eventtimeout->data = data;
350 	if(array_append(event->timeouts, &eventtimeout) != 0)
351 	{
352 		object_delete(eventtimeout);
353 		return -1;
354 	}
355 	if(event->timeout.tv_sec > timeout->tv_sec
356 			|| (event->timeout.tv_sec == timeout->tv_sec
357 				&& event->timeout.tv_usec > timeout->tv_usec))
358 	{
359 #ifdef DEBUG
360 		fprintf(stderr, "DEBUG: %s%s%ld%s%ld%s", __func__, "() tv_sec=",
361 				(long)timeout->tv_sec, ", tv_usec=",
362 				(long)timeout->tv_usec, "\n");
363 #endif
364 		event->timeout.tv_sec = timeout->tv_sec;
365 		event->timeout.tv_usec = timeout->tv_usec;
366 	}
367 	return 0;
368 }
369 
370 
371 /* event_unregister_io_read */
372 static int _unregister_io(eventioArray * eios, fd_set * fds, int fd);
373 
event_unregister_io_read(Event * event,int fd)374 int event_unregister_io_read(Event * event, int fd)
375 {
376 	event->fdmax = _unregister_io(event->reads, &event->rfds, fd);
377 	event->fdmax = max(event->fdmax, _unregister_io(event->writes, NULL,
378 				-1));
379 	return 0;
380 }
381 
382 
383 /* event_unregister_io_write */
event_unregister_io_write(Event * event,int fd)384 int event_unregister_io_write(Event * event, int fd)
385 {
386 	event->fdmax = _unregister_io(event->writes, &event->wfds, fd);
387 	event->fdmax = max(event->fdmax, _unregister_io(event->reads, NULL,
388 				-1));
389 	return 0;
390 }
391 
_unregister_io(eventioArray * eios,fd_set * fds,int fd)392 static int _unregister_io(eventioArray * eios, fd_set * fds, int fd)
393 {
394 	unsigned int i = 0;
395 	EventIO * eio;
396 	int fdmax = -1;
397 
398 	while(i < array_count(eios))
399 	{
400 		array_get_copy(eios, i, &eio);
401 		if(eio->fd != fd)
402 		{
403 			fdmax = max(fdmax, eio->fd);
404 			i++;
405 			continue;
406 		}
407 		FD_CLR(fd, fds);
408 		array_remove_pos(eios, i);
409 		object_delete(eio);
410 	}
411 	return fdmax;
412 }
413 
414 
415 /* event_unregister_timeout */
event_unregister_timeout(Event * event,EventTimeoutFunc func)416 int event_unregister_timeout(Event * event, EventTimeoutFunc func)
417 {
418 	unsigned int i = 0;
419 	EventTimeout * et;
420 	struct timeval now;
421 
422 	while(i < array_count(event->timeouts))
423 	{
424 		array_get_copy(event->timeouts, i, &et);
425 		if(et->func != func)
426 		{
427 			i++;
428 			continue;
429 		}
430 		array_remove_pos(event->timeouts, i);
431 		object_delete(et);
432 	}
433 	if(gettimeofday(&now, NULL) != 0)
434 		return error_set_code(1, "%s", strerror(errno));
435 	event->timeout.tv_sec = (time_t)LONG_MAX;
436 	event->timeout.tv_usec = (suseconds_t)LONG_MAX;
437 	for(i = 0; i < array_count(event->timeouts); i++)
438 	{
439 		array_get_copy(event->timeouts, i, &et);
440 		if(et->timeout.tv_sec < event->timeout.tv_sec
441 				|| (et->timeout.tv_sec == event->timeout.tv_sec
442 					&& et->timeout.tv_usec
443 					< event->timeout.tv_usec))
444 		{
445 			if((event->timeout.tv_sec = et->timeout.tv_sec
446 						- now.tv_sec) < 0)
447 			{
448 				event->timeout.tv_sec = 0;
449 				event->timeout.tv_usec = 0;
450 				break;
451 			}
452 			event->timeout.tv_usec = et->timeout.tv_usec
453 				- now.tv_usec;
454 			if(event->timeout.tv_usec >= 0)
455 				continue;
456 			event->timeout.tv_sec = max(0, event->timeout.tv_sec-1);
457 			event->timeout.tv_usec = -event->timeout.tv_usec;
458 		}
459 	}
460 	return 0;
461 }
462