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
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
206static unsigned
207gloopio_poll(void* impl)
208{
209    GLoopIo* io = impl;
210    return io->ready;
211}
212
213static 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,
229    gloopio_poll,
230    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;
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
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 */
270    int          numActiveIos;  /* number of active LoopIo objects */
271
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;
300    *pnode         = tt;
301}
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{
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
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
362static int
363glooper_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
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
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