• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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"
19 #include "sockets.h"
20 #include <inttypes.h>
21 #include <limits.h>
22 #include <errno.h>
23 
24 /**********************************************************************
25  **********************************************************************
26  *****
27  *****  T I M E R S
28  *****
29  **********************************************************************
30  **********************************************************************/
31 
32 typedef struct GLoopTimer GLoopTimer;
33 typedef struct GLoopIo    GLoopIo;
34 typedef struct GLooper    GLooper;
35 
36 struct GLoopTimer {
37     Duration      deadline;
38     LoopTimerFunc callback;
39     void*         opaque;
40     GLooper*      looper;
41     GLoopTimer*   activeNext;
42 };
43 
44 static Duration glooper_now(Looper* ll);
45 
46 static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer);
47 static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer);
48 static void glooper_addTimer(GLooper* looper, GLoopTimer* timer);
49 static void glooper_delTimer(GLooper* looper, GLoopTimer* timer);
50 
51 static void
glooptimer_stop(void * impl)52 glooptimer_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 
61 static void
glooptimer_startAbsolute(void * impl,Duration deadline_ms)62 glooptimer_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 
78 static void
glooptimer_startRelative(void * impl,Duration timeout_ms)79 glooptimer_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 
90 static int
glooptimer_isActive(void * impl)91 glooptimer_isActive(void* impl)
92 {
93     GLoopTimer*  tt = impl;
94     return (tt->deadline != DURATION_INFINITE);
95 }
96 
97 static void
glooptimer_free(void * impl)98 glooptimer_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 
109 static const LoopTimerClass  glooptimer_class = {
110     glooptimer_startRelative,
111     glooptimer_startAbsolute,
112     glooptimer_stop,
113     glooptimer_isActive,
114     glooptimer_free
115 };
116 
117 static void
glooper_timer_init(Looper * looper,LoopTimer * timer,LoopTimerFunc callback,void * opaque)118 glooper_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 
146 struct GLoopIo {
147     int         fd;
148     LoopIoFunc  callback;
149     void*       opaque;
150     unsigned    wanted;
151     unsigned    ready;
152     GLooper*    looper;
153 };
154 
155 static void glooper_delPendingIo(GLooper* looper, GLoopIo* io);
156 static void glooper_addIo(GLooper* looper, GLoopIo* io);
157 static void glooper_delIo(GLooper* looper, GLoopIo* io);
158 static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted);
159 
160 /* used to indicate that the set of wanted flags has changed */
161 static void
gloopio_modify(GLoopIo * io,unsigned wanted)162 gloopio_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 
178 static void
gloopio_wantRead(void * impl)179 gloopio_wantRead(void* impl)
180 {
181     GLoopIo* io = impl;
182     gloopio_modify(io, io->wanted | LOOP_IO_READ);
183 }
184 
185 static void
gloopio_wantWrite(void * impl)186 gloopio_wantWrite(void* impl)
187 {
188     GLoopIo* io = impl;
189     gloopio_modify(io, io->wanted | LOOP_IO_WRITE);
190 }
191 
192 static void
gloopio_dontWantRead(void * impl)193 gloopio_dontWantRead(void* impl)
194 {
195     GLoopIo* io = impl;
196     gloopio_modify(io, io->wanted & ~LOOP_IO_READ);
197 }
198 
199 static void
gloopio_dontWantWrite(void * impl)200 gloopio_dontWantWrite(void* impl)
201 {
202     GLoopIo* io = impl;
203     gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
204 }
205 
206 static unsigned
gloopio_poll(void * impl)207 gloopio_poll(void* impl)
208 {
209     GLoopIo* io = impl;
210     return io->ready;
211 }
212 
213 static void
gloopio_free(void * impl)214 gloopio_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 
224 static LoopIoClass  gloopio_class = {
225     gloopio_wantRead,
226     gloopio_wantWrite,
227     gloopio_dontWantRead,
228     gloopio_dontWantWrite,
229     gloopio_poll,
230     gloopio_free
231 };
232 
233 static void
glooper_io_init(Looper * looper,LoopIo * user,int fd,LoopIoFunc callback,void * opaque)234 glooper_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;
243     io->looper   = (GLooper*) looper;
244     io->wanted   = 0;
245     io->ready    = 0;
246 
247     socket_set_nonblock(fd);
248 
249     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 
263 struct 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 */
270     int          numActiveIos;  /* number of active LoopIo objects */
271 
272     IoLooper*    iolooper;
273     int          running;
274 };
275 
276 static void
glooper_addTimer(GLooper * looper,GLoopTimer * tt)277 glooper_addTimer(GLooper* looper, GLoopTimer* tt)
278 {
279     arefSet_add(looper->timers, tt);
280 }
281 
282 static void
glooper_delTimer(GLooper * looper,GLoopTimer * tt)283 glooper_delTimer(GLooper* looper, GLoopTimer* tt)
284 {
285     arefSet_del(looper->timers, tt);
286 }
287 
288 static void
glooper_addActiveTimer(GLooper * looper,GLoopTimer * tt)289 glooper_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;
300     *pnode         = tt;
301 }
302 
303 static void
glooper_delActiveTimer(GLooper * looper,GLoopTimer * tt)304 glooper_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 
319 static void
glooper_addIo(GLooper * looper,GLoopIo * io)320 glooper_addIo(GLooper* looper, GLoopIo* io)
321 {
322     arefSet_add(looper->ios, io);
323 }
324 
325 static void
glooper_delIo(GLooper * looper,GLoopIo * io)326 glooper_delIo(GLooper* looper, GLoopIo* io)
327 {
328     arefSet_del(looper->ios, io);
329 }
330 
331 static void
glooper_delPendingIo(GLooper * looper,GLoopIo * io)332 glooper_delPendingIo(GLooper* looper, GLoopIo* io)
333 {
334     arefSet_del(looper->pendingIos, io);
335 }
336 
337 static void
glooper_modifyFd(GLooper * looper,int fd,int oldWanted,int newWanted)338 glooper_modifyFd(GLooper* looper, int fd, int oldWanted, int newWanted)
339 {
340     if (oldWanted == 0 && newWanted != 0)
341         looper->numActiveIos += 1;
342 
343     if (oldWanted != 0 && newWanted == 0)
344         looper->numActiveIos -= 1;
345 
346     iolooper_modify(looper->iolooper, fd, oldWanted, newWanted);
347 }
348 
349 static Duration
glooper_now(Looper * ll)350 glooper_now(Looper*  ll)
351 {
352     return iolooper_now();
353 }
354 
355 static void
glooper_forceQuit(Looper * ll)356 glooper_forceQuit(Looper* ll)
357 {
358     GLooper*  looper = (GLooper*)ll;
359     looper->running = 0;
360 }
361 
362 static int
glooper_run(Looper * ll,Duration loop_deadline_ms)363 glooper_run(Looper*  ll, Duration loop_deadline_ms)
364 {
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 
374         /* 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 
380         /* First, compute next deadline */
381         Duration  deadline = DURATION_INFINITE;
382 
383         if (looper->activeTimers != NULL)
384             deadline = looper->activeTimers->deadline;
385 
386         if (deadline > loop_deadline_ms)
387             deadline = loop_deadline_ms;
388 
389         ret = iolooper_wait_absolute(iol, deadline);
390         if (ret < 0) { /* error, force stop ! */
391             break;
392         }
393         if (ret > 0) {
394             unsigned ready;
395             GLoopIo* io;
396 
397             /* Add io waiters to the pending list */
398             AREFSET_FOREACH(looper->ios, io, {
399                 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         {
451             GLoopIo* io;
452             AREFSET_FOREACH(looper->pendingIos,io,{
453                 io->callback(io->opaque,io->fd,io->ready);
454             });
455             arefSet_clear(looper->pendingIos);
456         }
457 
458         if (deadline > loop_deadline_ms)
459             return ETIMEDOUT;
460     }
461     return 0;
462 }
463 
464 static void
glooper_free(Looper * ll)465 glooper_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 
looper_newGeneric(void)481 Looper*  looper_newGeneric(void)
482 {
483     GLooper*  looper;
484 
485     ANEW0(looper);
486 
487     looper->iolooper = iolooper_new();
488 
489     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 }
502