diff options
author | David 'Digit' Turner <digit@android.com> | 2010-11-17 17:58:29 +0100 |
---|---|---|
committer | David 'Digit' Turner <digit@android.com> | 2010-11-18 09:32:28 +0100 |
commit | 7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9 (patch) | |
tree | ebfcf03154525d19357b25c8dea82c2b42fd0bad /android/looper-generic.c | |
parent | f6cbbf529c7f4d3164243fc2cb9241978b954633 (diff) | |
download | external_qemu-7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9.zip external_qemu-7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9.tar.gz external_qemu-7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9.tar.bz2 |
Implementation of event loop abstraction.
This patch adds "android/looper.h" which provides an
abstraction for event loops:
- android/looper-qemu.c implements it on top of the QEMU main event loop.
- android/looper-generic.c implements it on top of an IoLooper object.
The main idea is to move the UI-related code to use the abstraction
to handle timers and asynchronous (network) i/o.
NOTE: Code compiles but has not been heavily tested.
Change-Id: Ib6820c1b9a9950dc22449a332bc1b066a07af203
Diffstat (limited to 'android/looper-generic.c')
-rw-r--r-- | android/looper-generic.c | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/android/looper-generic.c b/android/looper-generic.c new file mode 100644 index 0000000..38d8ab5 --- /dev/null +++ b/android/looper-generic.c @@ -0,0 +1,466 @@ +/* Copyright (C) 2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#include "android/utils/assert.h" +#include "android/utils/reflist.h" +#include "android/utils/refset.h" +#include "android/utils/system.h" +#include "android/looper.h" +#include "iolooper.h" +#include <inttypes.h> +#include <limits.h> + +/********************************************************************** + ********************************************************************** + ***** + ***** T I M E R S + ***** + ********************************************************************** + **********************************************************************/ + +typedef struct GLoopTimer GLoopTimer; +typedef struct GLoopIo GLoopIo; +typedef struct GLooper GLooper; + +struct GLoopTimer { + Duration deadline; + LoopTimerFunc callback; + void* opaque; + GLooper* looper; + GLoopTimer* activeNext; +}; + +static Duration glooper_now(Looper* ll); + +static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer); +static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer); +static void glooper_addTimer(GLooper* looper, GLoopTimer* timer); +static void glooper_delTimer(GLooper* looper, GLoopTimer* timer); + +static void +glooptimer_stop(void* impl) +{ + GLoopTimer* tt = impl; + if (tt->deadline != DURATION_INFINITE) { + glooper_delActiveTimer(tt->looper, tt); + tt->deadline = DURATION_INFINITE; + } +} + +static void +glooptimer_startAbsolute(void* impl, Duration deadline_ms) +{ + GLoopTimer* tt = impl; + + /* Stop the timer if it was active */ + if (tt->deadline != DURATION_INFINITE) + glooptimer_stop(tt); + + /* Another way to stop a timer */ + if (deadline_ms == DURATION_INFINITE) + return; + + tt->deadline = deadline_ms; + glooper_addActiveTimer(tt->looper, tt); +} + +static void +glooptimer_startRelative(void* impl, Duration timeout_ms) +{ + GLoopTimer* tt = impl; + + if (timeout_ms == DURATION_INFINITE) { /* another way to stop the timer */ + glooptimer_stop(tt); + } else { + glooptimer_startAbsolute(tt, timeout_ms + glooper_now((Looper*)tt->looper)); + } +} + +static int +glooptimer_isActive(void* impl) +{ + GLoopTimer* tt = impl; + return (tt->deadline != DURATION_INFINITE); +} + +static void +glooptimer_free(void* impl) +{ + GLoopTimer* tt = impl; + + if (tt->deadline != DURATION_INFINITE) + glooptimer_stop(tt); + + glooper_delTimer(tt->looper, tt); + AFREE(tt); +} + +static const LoopTimerClass glooptimer_class = { + glooptimer_startRelative, + glooptimer_startAbsolute, + glooptimer_stop, + glooptimer_isActive, + glooptimer_free +}; + +static void +glooper_timer_init(Looper* looper, + LoopTimer* timer, + LoopTimerFunc callback, + void* opaque) +{ + GLoopTimer* tt; + + ANEW0(tt); + + tt->deadline = DURATION_INFINITE; + tt->callback = callback; + tt->opaque = opaque; + tt->looper = (GLooper*) looper; + + glooper_addTimer(tt->looper, tt); + + timer->impl = tt; + timer->clazz = (LoopTimerClass*) &glooptimer_class; +} + +/********************************************************************** + ********************************************************************** + ***** + ***** I / O + ***** + ********************************************************************** + **********************************************************************/ + +struct GLoopIo { + int fd; + LoopIoFunc callback; + void* opaque; + unsigned wanted; + unsigned ready; + GLooper* looper; +}; + +static void glooper_delPendingIo(GLooper* looper, GLoopIo* io); +static void glooper_addIo(GLooper* looper, GLoopIo* io); +static void glooper_delIo(GLooper* looper, GLoopIo* io); +static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted); + +/* used to indicate that the set of wanted flags has changed */ +static void +gloopio_modify(GLoopIo* io, unsigned wanted) +{ + /* If nothing changed, return */ + if (io->wanted == wanted) + return; + + /* If we are pending, and we're not interested by the + * current ready flags, remove from list */ + if (io->ready != 0 && (io->ready & wanted) == 0) { + glooper_delPendingIo(io->looper, io); + } + io->ready &= wanted; + glooper_modifyFd(io->looper, io->fd, io->wanted, wanted); + io->wanted = wanted; +} + +static void +gloopio_wantRead(void* impl) +{ + GLoopIo* io = impl; + gloopio_modify(io, io->wanted | LOOP_IO_READ); +} + +static void +gloopio_wantWrite(void* impl) +{ + GLoopIo* io = impl; + gloopio_modify(io, io->wanted | LOOP_IO_WRITE); +} + +static void +gloopio_dontWantRead(void* impl) +{ + GLoopIo* io = impl; + gloopio_modify(io, io->wanted & ~LOOP_IO_READ); +} + +static void +gloopio_dontWantWrite(void* impl) +{ + GLoopIo* io = impl; + gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE); +} + +static void +gloopio_free(void* impl) +{ + GLoopIo* io = impl; + if (io->ready != 0) + glooper_delPendingIo(io->looper, io); + + glooper_delIo(io->looper, io); + AFREE(io); +} + +static LoopIoClass gloopio_class = { + gloopio_wantRead, + gloopio_wantWrite, + gloopio_dontWantRead, + gloopio_dontWantWrite, + gloopio_free +}; + +static void +glooper_io_init(Looper* looper, LoopIo* user, int fd, LoopIoFunc callback, void* opaque) +{ + GLooper* gg = (GLooper*)looper; + GLoopIo* io; + + ANEW0(io); + io->fd = fd; + io->callback = callback; + io->opaque = opaque; + io->wanted = 0; + io->ready = 0; + + glooper_addIo(gg, io); + + user->impl = io; + user->clazz = (LoopIoClass*) &gloopio_class; +} + +/********************************************************************** + ********************************************************************** + ***** + ***** L O O P E R + ***** + ********************************************************************** + **********************************************************************/ + +struct GLooper { + Looper looper; + ARefSet timers[1]; /* set of all timers */ + GLoopTimer* activeTimers; /* sorted list of active timers */ + + ARefSet ios[1]; /* set of all i/o waiters */ + ARefSet pendingIos[1]; /* list of pending i/o waiters */ + + IoLooper* iolooper; + int running; +}; + +static void +glooper_addTimer(GLooper* looper, GLoopTimer* tt) +{ + arefSet_add(looper->timers, tt); +} + +static void +glooper_delTimer(GLooper* looper, GLoopTimer* tt) +{ + arefSet_del(looper->timers, tt); +} + +static void +glooper_addActiveTimer(GLooper* looper, GLoopTimer* tt) +{ + Duration deadline = tt->deadline; + GLoopTimer** pnode = &looper->activeTimers; + for (;;) { + GLoopTimer* node = *pnode; + if (node == NULL || node->deadline > deadline) + break; + pnode = &node->activeNext; + } + tt->activeNext = *pnode; + *pnode = tt->activeNext; +} + +static void +glooper_delActiveTimer(GLooper* looper, GLoopTimer* tt) +{ + GLoopTimer** pnode = &looper->activeTimers; + for (;;) { + if (*pnode == NULL) + break; + if (*pnode == tt) { + *pnode = tt->activeNext; + tt->activeNext = NULL; + break; + } + pnode = &(*pnode)->activeNext; + } +} + +static void +glooper_addIo(GLooper* looper, GLoopIo* io) +{ + arefSet_add(looper->ios, io); +} + +static void +glooper_delIo(GLooper* looper, GLoopIo* io) +{ + arefSet_del(looper->ios, io); +} + +static void +glooper_delPendingIo(GLooper* looper, GLoopIo* io) +{ + arefSet_del(looper->pendingIos, io); +} + +static void +glooper_modifyFd(GLooper* looper, int fd, int oldWanted, int newWanted) +{ + iolooper_modify(looper->iolooper, fd, oldWanted, newWanted); +} + +static Duration +glooper_now(Looper* ll) +{ + return iolooper_now(); +} + +static void +glooper_forceQuit(Looper* ll) +{ + GLooper* looper = (GLooper*)ll; + looper->running = 0; +} + +static void +glooper_run(Looper* ll) +{ + GLooper* looper = (GLooper*) ll; + IoLooper* iol = looper->iolooper; + + looper->running = 1; + + while (looper->running) + { + int ret; + + /* First, compute next deadline */ + Duration deadline = DURATION_INFINITE; + + if (looper->activeTimers != NULL) + deadline = looper->activeTimers->deadline; + + ret = iolooper_wait_absolute(iol, deadline); + if (ret < 0) { /* error, force stop ! */ + break; + } + if (ret > 0) { + unsigned ready; + + /* Add io waiters to the pending list */ + AREFSET_FOREACH(looper->ios, io_, { + GLoopIo* io = io_; + if (io->wanted == 0) + continue; + + ready = 0; + + if (iolooper_is_read(iol, io->fd)) + ready |= LOOP_IO_READ; + + if (iolooper_is_write(iol, io->fd)) + ready |= LOOP_IO_WRITE; + + io->ready = ready; + if (ready != 0) { + arefSet_add(looper->pendingIos, io); + } + }); + } + + /* Do we have any expired timers here ? */ + GLoopTimer* pendingTimers = NULL; + GLoopTimer** pendingLastP = &pendingTimers; + + deadline = iolooper_now(); + for (;;) { + GLoopTimer* timer = looper->activeTimers; + if (timer == NULL || timer->deadline > deadline) + break; + + /* remove from active list, and append to pending one */ + timer->deadline = DURATION_INFINITE; + looper->activeTimers = timer->activeNext; + + *pendingLastP = timer; + timer->activeNext = NULL; + pendingLastP = &timer->activeNext; + } + + /* Fire the pending timers, if any. We do that in a separate + * step because the callbacks could modify the active list + * by starting/stopping other timers. + */ + { + GLoopTimer* timer; + while ((timer = pendingTimers) != NULL) { + pendingTimers = timer->activeNext; + timer->activeNext = NULL; + timer->callback(timer->opaque); + } + } + + /* Now fire the pending ios */ + { + AREFSET_FOREACH(looper->pendingIos,io_,{ + GLoopIo* io = io_; + io->callback(io->opaque,io->fd,io->ready); + }); + arefSet_clear(looper->pendingIos); + } + } +} + +static void +glooper_free(Looper* ll) +{ + GLooper* looper = (GLooper*)ll; + + arefSet_done(looper->timers); + looper->activeTimers = NULL; + + arefSet_done(looper->ios); + arefSet_done(looper->pendingIos); + + iolooper_free(looper->iolooper); + looper->iolooper = NULL; + + AFREE(looper); +} + +Looper* looper_newGeneric(void) +{ + GLooper* looper; + + ANEW0(looper); + + looper->looper.now = glooper_now; + looper->looper.timer_init = glooper_timer_init; + looper->looper.io_init = glooper_io_init; + looper->looper.run = glooper_run; + looper->looper.forceQuit = glooper_forceQuit; + looper->looper.destroy = glooper_free; + + /* Our implementation depends on these values being equal */ + AASSERT_INT(LOOP_IO_READ, IOLOOPER_READ); + AASSERT_INT(LOOP_IO_WRITE, IOLOOPER_WRITE); + + return &looper->looper; +} |