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