Home | History | Annotate | Download | only in android
      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 "android/iolooper.h"
     19 #include "android/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
     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
     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
     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
     91 glooptimer_isActive(void* impl)
     92 {
     93     GLoopTimer*  tt = impl;
     94     return (tt->deadline != DURATION_INFINITE);
     95 }
     96 
     97 static void
     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
    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
    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
    179 gloopio_wantRead(void* impl)
    180 {
    181     GLoopIo* io = impl;
    182     gloopio_modify(io, io->wanted | LOOP_IO_READ);
    183 }
    184 
    185 static void
    186 gloopio_wantWrite(void* impl)
    187 {
    188     GLoopIo* io = impl;
    189     gloopio_modify(io, io->wanted | LOOP_IO_WRITE);
    190 }
    191 
    192 static void
    193 gloopio_dontWantRead(void* impl)
    194 {
    195     GLoopIo* io = impl;
    196     gloopio_modify(io, io->wanted & ~LOOP_IO_READ);
    197 }
    198 
    199 static void
    200 gloopio_dontWantWrite(void* impl)
    201 {
    202     GLoopIo* io = impl;
    203     gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
    204 }
    205 
    206 static unsigned
    207 gloopio_poll(void* impl)
    208 {
    209     GLoopIo* io = impl;
    210     return io->ready;
    211 }
    212 
    213 static void
    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
    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
    277 glooper_addTimer(GLooper* looper, GLoopTimer* tt)
    278 {
    279     arefSet_add(looper->timers, tt);
    280 }
    281 
    282 static void
    283 glooper_delTimer(GLooper* looper, GLoopTimer* tt)
    284 {
    285     arefSet_del(looper->timers, tt);
    286 }
    287 
    288 static void
    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
    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
    320 glooper_addIo(GLooper* looper, GLoopIo* io)
    321 {
    322     arefSet_add(looper->ios, io);
    323 }
    324 
    325 static void
    326 glooper_delIo(GLooper* looper, GLoopIo* io)
    327 {
    328     arefSet_del(looper->ios, io);
    329 }
    330 
    331 static void
    332 glooper_delPendingIo(GLooper* looper, GLoopIo* io)
    333 {
    334     arefSet_del(looper->pendingIos, io);
    335 }
    336 
    337 static void
    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
    350 glooper_now(Looper*  ll)
    351 {
    352     return iolooper_now();
    353 }
    354 
    355 static void
    356 glooper_forceQuit(Looper* ll)
    357 {
    358     GLooper*  looper = (GLooper*)ll;
    359     looper->running = 0;
    360 }
    361 
    362 static int
    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
    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 
    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