blob: ddc08248c50de38d14439522924ffc2513fdbd15 [file] [log] [blame]
David 'Digit' Turner7a17b602010-11-17 17:58:29 +01001/* Copyright (C) 2010 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12
13#include "android/utils/assert.h"
14#include "android/utils/reflist.h"
15#include "android/utils/refset.h"
16#include "android/utils/system.h"
17#include "android/looper.h"
18#include "iolooper.h"
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +010019#include "sockets.h"
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010020#include <inttypes.h>
21#include <limits.h>
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +010022#include <errno.h>
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010023
24/**********************************************************************
25 **********************************************************************
26 *****
27 ***** T I M E R S
28 *****
29 **********************************************************************
30 **********************************************************************/
31
32typedef struct GLoopTimer GLoopTimer;
33typedef struct GLoopIo GLoopIo;
34typedef struct GLooper GLooper;
35
36struct GLoopTimer {
37 Duration deadline;
38 LoopTimerFunc callback;
39 void* opaque;
40 GLooper* looper;
41 GLoopTimer* activeNext;
42};
43
44static Duration glooper_now(Looper* ll);
45
46static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer);
47static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer);
48static void glooper_addTimer(GLooper* looper, GLoopTimer* timer);
49static void glooper_delTimer(GLooper* looper, GLoopTimer* timer);
50
51static void
52glooptimer_stop(void* impl)
53{
54 GLoopTimer* tt = impl;
55 if (tt->deadline != DURATION_INFINITE) {
56 glooper_delActiveTimer(tt->looper, tt);
57 tt->deadline = DURATION_INFINITE;
58 }
59}
60
61static void
62glooptimer_startAbsolute(void* impl, Duration deadline_ms)
63{
64 GLoopTimer* tt = impl;
65
66 /* Stop the timer if it was active */
67 if (tt->deadline != DURATION_INFINITE)
68 glooptimer_stop(tt);
69
70 /* Another way to stop a timer */
71 if (deadline_ms == DURATION_INFINITE)
72 return;
73
74 tt->deadline = deadline_ms;
75 glooper_addActiveTimer(tt->looper, tt);
76}
77
78static void
79glooptimer_startRelative(void* impl, Duration timeout_ms)
80{
81 GLoopTimer* tt = impl;
82
83 if (timeout_ms == DURATION_INFINITE) { /* another way to stop the timer */
84 glooptimer_stop(tt);
85 } else {
86 glooptimer_startAbsolute(tt, timeout_ms + glooper_now((Looper*)tt->looper));
87 }
88}
89
90static int
91glooptimer_isActive(void* impl)
92{
93 GLoopTimer* tt = impl;
94 return (tt->deadline != DURATION_INFINITE);
95}
96
97static void
98glooptimer_free(void* impl)
99{
100 GLoopTimer* tt = impl;
101
102 if (tt->deadline != DURATION_INFINITE)
103 glooptimer_stop(tt);
104
105 glooper_delTimer(tt->looper, tt);
106 AFREE(tt);
107}
108
109static const LoopTimerClass glooptimer_class = {
110 glooptimer_startRelative,
111 glooptimer_startAbsolute,
112 glooptimer_stop,
113 glooptimer_isActive,
114 glooptimer_free
115};
116
117static void
118glooper_timer_init(Looper* looper,
119 LoopTimer* timer,
120 LoopTimerFunc callback,
121 void* opaque)
122{
123 GLoopTimer* tt;
124
125 ANEW0(tt);
126
127 tt->deadline = DURATION_INFINITE;
128 tt->callback = callback;
129 tt->opaque = opaque;
130 tt->looper = (GLooper*) looper;
131
132 glooper_addTimer(tt->looper, tt);
133
134 timer->impl = tt;
135 timer->clazz = (LoopTimerClass*) &glooptimer_class;
136}
137
138/**********************************************************************
139 **********************************************************************
140 *****
141 ***** I / O
142 *****
143 **********************************************************************
144 **********************************************************************/
145
146struct GLoopIo {
147 int fd;
148 LoopIoFunc callback;
149 void* opaque;
150 unsigned wanted;
151 unsigned ready;
152 GLooper* looper;
153};
154
155static void glooper_delPendingIo(GLooper* looper, GLoopIo* io);
156static void glooper_addIo(GLooper* looper, GLoopIo* io);
157static void glooper_delIo(GLooper* looper, GLoopIo* io);
158static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted);
159
160/* used to indicate that the set of wanted flags has changed */
161static void
162gloopio_modify(GLoopIo* io, unsigned wanted)
163{
164 /* If nothing changed, return */
165 if (io->wanted == wanted)
166 return;
167
168 /* If we are pending, and we're not interested by the
169 * current ready flags, remove from list */
170 if (io->ready != 0 && (io->ready & wanted) == 0) {
171 glooper_delPendingIo(io->looper, io);
172 }
173 io->ready &= wanted;
174 glooper_modifyFd(io->looper, io->fd, io->wanted, wanted);
175 io->wanted = wanted;
176}
177
178static void
179gloopio_wantRead(void* impl)
180{
181 GLoopIo* io = impl;
182 gloopio_modify(io, io->wanted | LOOP_IO_READ);
183}
184
185static void
186gloopio_wantWrite(void* impl)
187{
188 GLoopIo* io = impl;
189 gloopio_modify(io, io->wanted | LOOP_IO_WRITE);
190}
191
192static void
193gloopio_dontWantRead(void* impl)
194{
195 GLoopIo* io = impl;
196 gloopio_modify(io, io->wanted & ~LOOP_IO_READ);
197}
198
199static void
200gloopio_dontWantWrite(void* impl)
201{
202 GLoopIo* io = impl;
203 gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
204}
205
David 'Digit' Turnera65e4152011-04-28 16:29:57 +0200206static unsigned
207gloopio_poll(void* impl)
208{
209 GLoopIo* io = impl;
210 return io->ready;
211}
212
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100213static void
214gloopio_free(void* impl)
215{
216 GLoopIo* io = impl;
217 if (io->ready != 0)
218 glooper_delPendingIo(io->looper, io);
219
220 glooper_delIo(io->looper, io);
221 AFREE(io);
222}
223
224static LoopIoClass gloopio_class = {
225 gloopio_wantRead,
226 gloopio_wantWrite,
227 gloopio_dontWantRead,
228 gloopio_dontWantWrite,
David 'Digit' Turnera65e4152011-04-28 16:29:57 +0200229 gloopio_poll,
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100230 gloopio_free
231};
232
233static void
234glooper_io_init(Looper* looper, LoopIo* user, int fd, LoopIoFunc callback, void* opaque)
235{
236 GLooper* gg = (GLooper*)looper;
237 GLoopIo* io;
238
239 ANEW0(io);
240 io->fd = fd;
241 io->callback = callback;
242 io->opaque = opaque;
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100243 io->looper = (GLooper*) looper;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100244 io->wanted = 0;
245 io->ready = 0;
246
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100247 socket_set_nonblock(fd);
248
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100249 glooper_addIo(gg, io);
250
251 user->impl = io;
252 user->clazz = (LoopIoClass*) &gloopio_class;
253}
254
255/**********************************************************************
256 **********************************************************************
257 *****
258 ***** L O O P E R
259 *****
260 **********************************************************************
261 **********************************************************************/
262
263struct GLooper {
264 Looper looper;
265 ARefSet timers[1]; /* set of all timers */
266 GLoopTimer* activeTimers; /* sorted list of active timers */
267
268 ARefSet ios[1]; /* set of all i/o waiters */
269 ARefSet pendingIos[1]; /* list of pending i/o waiters */
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100270 int numActiveIos; /* number of active LoopIo objects */
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100271
272 IoLooper* iolooper;
273 int running;
274};
275
276static void
277glooper_addTimer(GLooper* looper, GLoopTimer* tt)
278{
279 arefSet_add(looper->timers, tt);
280}
281
282static void
283glooper_delTimer(GLooper* looper, GLoopTimer* tt)
284{
285 arefSet_del(looper->timers, tt);
286}
287
288static void
289glooper_addActiveTimer(GLooper* looper, GLoopTimer* tt)
290{
291 Duration deadline = tt->deadline;
292 GLoopTimer** pnode = &looper->activeTimers;
293 for (;;) {
294 GLoopTimer* node = *pnode;
295 if (node == NULL || node->deadline > deadline)
296 break;
297 pnode = &node->activeNext;
298 }
299 tt->activeNext = *pnode;
David 'Digit' Turner07db3492011-02-02 17:36:34 +0100300 *pnode = tt;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100301}
302
303static void
304glooper_delActiveTimer(GLooper* looper, GLoopTimer* tt)
305{
306 GLoopTimer** pnode = &looper->activeTimers;
307 for (;;) {
308 if (*pnode == NULL)
309 break;
310 if (*pnode == tt) {
311 *pnode = tt->activeNext;
312 tt->activeNext = NULL;
313 break;
314 }
315 pnode = &(*pnode)->activeNext;
316 }
317}
318
319static void
320glooper_addIo(GLooper* looper, GLoopIo* io)
321{
322 arefSet_add(looper->ios, io);
323}
324
325static void
326glooper_delIo(GLooper* looper, GLoopIo* io)
327{
328 arefSet_del(looper->ios, io);
329}
330
331static void
332glooper_delPendingIo(GLooper* looper, GLoopIo* io)
333{
334 arefSet_del(looper->pendingIos, io);
335}
336
337static void
338glooper_modifyFd(GLooper* looper, int fd, int oldWanted, int newWanted)
339{
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100340 if (oldWanted == 0 && newWanted != 0)
341 looper->numActiveIos += 1;
342
343 if (oldWanted != 0 && newWanted == 0)
344 looper->numActiveIos -= 1;
345
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100346 iolooper_modify(looper->iolooper, fd, oldWanted, newWanted);
347}
348
349static Duration
350glooper_now(Looper* ll)
351{
352 return iolooper_now();
353}
354
355static void
356glooper_forceQuit(Looper* ll)
357{
358 GLooper* looper = (GLooper*)ll;
359 looper->running = 0;
360}
361
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100362static int
363glooper_run(Looper* ll, Duration loop_deadline_ms)
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100364{
365 GLooper* looper = (GLooper*) ll;
366 IoLooper* iol = looper->iolooper;
367
368 looper->running = 1;
369
370 while (looper->running)
371 {
372 int ret;
373
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100374 /* Exit prematurely if we detect that we don't have any active timer
375 * and no active LoopIo
376 */
377 if (looper->numActiveIos == 0 && looper->activeTimers == NULL)
378 return EWOULDBLOCK;
379
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100380 /* First, compute next deadline */
381 Duration deadline = DURATION_INFINITE;
382
383 if (looper->activeTimers != NULL)
384 deadline = looper->activeTimers->deadline;
385
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100386 if (deadline > loop_deadline_ms)
387 deadline = loop_deadline_ms;
388
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100389 ret = iolooper_wait_absolute(iol, deadline);
390 if (ret < 0) { /* error, force stop ! */
391 break;
392 }
393 if (ret > 0) {
394 unsigned ready;
David 'Digit' Turner347753b2010-11-18 16:06:27 +0100395 GLoopIo* io;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100396
397 /* Add io waiters to the pending list */
David 'Digit' Turner347753b2010-11-18 16:06:27 +0100398 AREFSET_FOREACH(looper->ios, io, {
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100399 if (io->wanted == 0)
400 continue;
401
402 ready = 0;
403
404 if (iolooper_is_read(iol, io->fd))
405 ready |= LOOP_IO_READ;
406
407 if (iolooper_is_write(iol, io->fd))
408 ready |= LOOP_IO_WRITE;
409
410 io->ready = ready;
411 if (ready != 0) {
412 arefSet_add(looper->pendingIos, io);
413 }
414 });
415 }
416
417 /* Do we have any expired timers here ? */
418 GLoopTimer* pendingTimers = NULL;
419 GLoopTimer** pendingLastP = &pendingTimers;
420
421 deadline = iolooper_now();
422 for (;;) {
423 GLoopTimer* timer = looper->activeTimers;
424 if (timer == NULL || timer->deadline > deadline)
425 break;
426
427 /* remove from active list, and append to pending one */
428 timer->deadline = DURATION_INFINITE;
429 looper->activeTimers = timer->activeNext;
430
431 *pendingLastP = timer;
432 timer->activeNext = NULL;
433 pendingLastP = &timer->activeNext;
434 }
435
436 /* Fire the pending timers, if any. We do that in a separate
437 * step because the callbacks could modify the active list
438 * by starting/stopping other timers.
439 */
440 {
441 GLoopTimer* timer;
442 while ((timer = pendingTimers) != NULL) {
443 pendingTimers = timer->activeNext;
444 timer->activeNext = NULL;
445 timer->callback(timer->opaque);
446 }
447 }
448
449 /* Now fire the pending ios */
450 {
David 'Digit' Turner347753b2010-11-18 16:06:27 +0100451 GLoopIo* io;
452 AREFSET_FOREACH(looper->pendingIos,io,{
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100453 io->callback(io->opaque,io->fd,io->ready);
454 });
455 arefSet_clear(looper->pendingIos);
456 }
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100457
458 if (deadline > loop_deadline_ms)
459 return ETIMEDOUT;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100460 }
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100461 return 0;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100462}
463
464static void
465glooper_free(Looper* ll)
466{
467 GLooper* looper = (GLooper*)ll;
468
469 arefSet_done(looper->timers);
470 looper->activeTimers = NULL;
471
472 arefSet_done(looper->ios);
473 arefSet_done(looper->pendingIos);
474
475 iolooper_free(looper->iolooper);
476 looper->iolooper = NULL;
477
478 AFREE(looper);
479}
480
481Looper* looper_newGeneric(void)
482{
483 GLooper* looper;
484
485 ANEW0(looper);
486
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100487 looper->iolooper = iolooper_new();
488
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100489 looper->looper.now = glooper_now;
490 looper->looper.timer_init = glooper_timer_init;
491 looper->looper.io_init = glooper_io_init;
492 looper->looper.run = glooper_run;
493 looper->looper.forceQuit = glooper_forceQuit;
494 looper->looper.destroy = glooper_free;
495
496 /* Our implementation depends on these values being equal */
497 AASSERT_INT(LOOP_IO_READ, IOLOOPER_READ);
498 AASSERT_INT(LOOP_IO_WRITE, IOLOOPER_WRITE);
499
500 return &looper->looper;
501}