From 7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Wed, 17 Nov 2010 17:58:29 +0100 Subject: 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 --- Makefile.android | 10 +- android/looper-generic.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++ android/looper-qemu.c | 402 ++++++++++++++++++++++++++++++++++++++++ android/looper.h | 296 ++++++++++++++++++++++++++++++ android/utils/refset.h | 6 + android/utils/vector.h | 5 + iolooper-select.c | 23 +++ iolooper.h | 6 + 8 files changed, 1211 insertions(+), 3 deletions(-) create mode 100644 android/looper-generic.c create mode 100644 android/looper-qemu.c create mode 100644 android/looper.h diff --git a/Makefile.android b/Makefile.android index 870d01e..942b03d 100644 --- a/Makefile.android +++ b/Makefile.android @@ -757,12 +757,12 @@ UI_AND_CORE_SOURCES = \ android/utils/assert.c \ android/utils/bufprint.c \ android/utils/debug.c \ + android/utils/path.c \ android/utils/dirscanner.c \ android/utils/filelock.c \ android/utils/mapfile.c \ android/utils/misc.c \ android/utils/panic.c \ - android/utils/path.c \ android/utils/reflist.c \ android/utils/refset.c \ android/utils/stralloc.c \ @@ -863,6 +863,7 @@ endif VL_SOURCES := framebuffer.c \ user-events-qemu.c \ android/cmdline-option.c \ + android/looper-qemu.c \ android/main.c \ # Add common system libraries @@ -1038,6 +1039,8 @@ LOCAL_LDLIBS += $(AUDIO_LDLIBS) # VL_SOURCES := framebuffer.c \ user-events-qemu.c \ + android/looper-qemu.c \ + android/looper-generic.c \ # Add common system libraries # @@ -1186,12 +1189,13 @@ VL_SOURCES := framebuffer.c \ android/cmdline-option.c \ android/config.c \ android/display.c \ + android/looper-generic.c \ android/snapshot.c \ android/main.c \ qemu-timer-ui.c \ vl-android-ui.c \ - console-ui.c \ - iolooper-select.c \ + console-ui.c \ + iolooper-select.c \ # Add common system libraries # 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 +#include + +/********************************************************************** + ********************************************************************** + ***** + ***** 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; +} diff --git a/android/looper-qemu.c b/android/looper-qemu.c new file mode 100644 index 0000000..0e55658 --- /dev/null +++ b/android/looper-qemu.c @@ -0,0 +1,402 @@ +/* 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. +*/ + +/* Implement the Looper interface on top of the QEMU main event loop */ + +#include +#include "qemu-common.h" +#include "qemu-timer.h" +#include "qemu-char.h" +#include "sockets.h" /* for socket_set_nonblock() */ + +/********************************************************************** + ********************************************************************** + ***** + ***** T I M E R S + ***** + ********************************************************************** + **********************************************************************/ + +/* Model a timer simple as a QEMUTimer for the host_clock */ + +static void +qlooptimer_startRelative(void* impl, Duration timeout_ms) +{ + QEMUTimer* tt = impl; + if (timeout_ms == DURATION_INFINITE) + qemu_del_timer(tt); + else + qemu_mod_timer(tt, qemu_get_clock_ns(host_clock) + timeout_ms*1000000); +} + +static void +qlooptimer_startAbsolute(void* impl, Duration deadline_ms) +{ + QEMUTimer* tt = impl; + if (deadline_ms == DURATION_INFINITE) + qemu_del_timer(tt); + else + qemu_mod_timer(tt, deadline_ms*1000000); +} + +static void +qlooptimer_stop(void* impl) +{ + QEMUTimer* tt = impl; + qemu_del_timer(tt); +} + +static int +qlooptimer_isActive(void* impl) +{ + QEMUTimer* tt = impl; + return qemu_timer_pending(tt); +} + +static void +qlooptimer_free(void* impl) +{ + QEMUTimer* tt = impl; + qemu_free_timer(tt); +} + +static const LoopTimerClass qlooptimer_class = { + qlooptimer_startRelative, + qlooptimer_startAbsolute, + qlooptimer_stop, + qlooptimer_isActive, + qlooptimer_free +}; + +static void +qlooper_timer_init(Looper* looper, + LoopTimer* timer, + LoopTimerFunc callback, + void* opaque) +{ + timer->clazz = (LoopTimerClass*) &qlooptimer_class; + timer->impl = qemu_new_timer(host_clock, callback, opaque); +} + +/********************************************************************** + ********************************************************************** + ***** + ***** F I L E D E S C R I P T O R S + ***** + ********************************************************************** + **********************************************************************/ + +/* Modeling the LoopIo is a bit more complex because the main event loop + * will call different functions for read and write readiness, while our + * users expect a single call with a mask of ready events. + * + * Since the QEMU main event loop looks like the following: + * + * 1/ perform select() + * 2/ for each file descriptor: + * if readReady: + * call readHandler() + * if writeReady: + * call writeHandler() + * 3/ run timers + * 4/ run bottom-half handlers + * + * We're going to provide simple read and write handlers that only mark + * the file descriptor for readiness, and put it on a "pending list". + * + * Then, we're going to schedule a bottom-half handler when such a pending + * i/o event occurs, in order to call the user callback with the correct + * flags. + */ + +typedef struct QLoopIo QLoopIo; + +typedef struct QLooper QLooper; + +struct QLoopIo { + int fd; + LoopIoFunc user_callback; + void* user_opaque; + unsigned wanted; + unsigned ready; + QLooper* looper; + QLoopIo* pendingNext; + QLoopIo* next; +}; + +static void qlooper_addIo(QLooper* looper, QLoopIo* io); +static void qlooper_delIo(QLooper* looper, QLoopIo* io); + +static QLoopIo* +qloopio_new(int fd, LoopIoFunc callback, void* opaque, QLooper* qlooper) +{ + QLoopIo* io = qemu_malloc(sizeof(*io)); + + io->fd = fd; + io->user_callback = callback; + io->user_opaque = opaque; + io->wanted = 0; + io->ready = 0; + io->looper = qlooper; + io->pendingNext = NULL; + + qlooper_addIo(qlooper, io); + + return io; +} + +static void qlooper_addPendingIo(QLooper* qlooper, QLoopIo* io); +static void qlooper_delPendingIo(QLooper* qlooper, QLoopIo* io); + +static void +qloopio_removePending(QLoopIo* io) +{ + if (io->ready != 0) { + qlooper_delPendingIo(io->looper, io); + io->ready = 0; + } +} + +static void +qloopio_setReady(QLoopIo* io, unsigned flag) +{ + if (io->ready == 0) { + qlooper_addPendingIo(io->looper, io); + } + io->ready |= flag; +} + +static void +qloopio_handleRead(void* opaque) +{ + QLoopIo* io = opaque; + qloopio_setReady(io, LOOP_IO_READ); +} + +static void +qloopio_handleWrite(void* opaque) +{ + QLoopIo* io = opaque; + qloopio_setReady(io, LOOP_IO_WRITE); +} + +static void +qloopio_modify(QLoopIo* io, unsigned wanted) +{ + /* no change, don't bother */ + if (wanted == io->wanted) + return; + + /* if we're pending, but the new mask doesn't care about + * out state, remove from pending list */ + if (io->ready && (io->ready & wanted) == 0) + qloopio_removePending(io); + + /* recompute read/write handlers for QEMU */ + IOHandler* fd_read = (wanted & LOOP_IO_READ) ? qloopio_handleRead : NULL; + IOHandler* fd_write = (wanted & LOOP_IO_WRITE) ? qloopio_handleWrite : NULL; + qemu_set_fd_handler(io->fd, fd_read, fd_write, io); +} + +static void +qloopio_wantRead(void* impl) +{ + QLoopIo* io = impl; + qloopio_modify(io, io->wanted | LOOP_IO_READ); +} + +static void +qloopio_wantWrite(void* impl) +{ + QLoopIo* io = impl; + qloopio_modify(io, io->wanted | LOOP_IO_WRITE); +} + +static void +qloopio_dontWantRead(void* impl) +{ + QLoopIo* io = impl; + qloopio_modify(io, io->wanted & ~LOOP_IO_READ); +} + +static void +qloopio_dontWantWrite(void* impl) +{ + QLoopIo* io = impl; + qloopio_modify(io, io->wanted & ~LOOP_IO_WRITE); +} + +static void +qloopio_free(void* impl) +{ + QLoopIo* io = impl; + if (io->ready) + qloopio_removePending(io); + + /* remove from global list */ + qlooper_delIo(io->looper, io); + + /* make QEMU forget about this fd */ + qemu_set_fd_handler(io->fd, NULL, NULL, NULL); + io->fd = -1; + qemu_free(io); +} + +static const LoopIoClass qlooper_io_class = { + qloopio_wantRead, + qloopio_wantWrite, + qloopio_dontWantRead, + qloopio_dontWantWrite, + qloopio_free +}; + +static void +qlooper_io_init(Looper* looper, + LoopIo* loopio, + int fd, + LoopIoFunc callback, + void* opaque) +{ + QLoopIo* io = qloopio_new(fd, callback, opaque, (QLooper*)looper); + + socket_set_nonblock(fd); + + loopio->clazz = (LoopIoClass*) &qlooper_io_class; + loopio->impl = io; +} + +struct QLooper { + Looper looper; + QLoopIo* io_list; + QLoopIo* io_pending; + QEMUBH* io_bh; +}; + +static void +qlooper_addIo(QLooper* looper, QLoopIo* io) +{ + io->next = looper->io_list; + looper->io_list = io; +} + +static void +qlooper_delIo(QLooper* looper, QLoopIo* io) +{ + QLoopIo** pnode = &looper->io_list; + for (;;) { + if (*pnode == NULL) + break; + if (*pnode == io) { + *pnode = io->next; + io->next = NULL; + break; + } + pnode = &(*pnode)->next; + } +} + +static void +qlooper_addPendingIo(QLooper* looper, QLoopIo* io) +{ + if (looper->io_pending == NULL) { + qemu_bh_schedule(looper->io_bh); + } + io->pendingNext = looper->io_pending; + looper->io_pending = io->pendingNext; +} + +static void +qlooper_delPendingIo(QLooper* looper, QLoopIo* io) +{ + QLoopIo** pnode = &looper->io_pending; + for (;;) { + if (*pnode == NULL) + break; + if (*pnode == io) { + *pnode = io->pendingNext; + break; + } + pnode = &(*pnode)->pendingNext; + } + io->pendingNext = NULL; +} + +/* This function is called by the main event loop when pending i/o + * events were registered with qlooper_addPendingIo(). It will parse + * the list of pending QLoopIo and call the user callback with the + * appropriate flags. + */ +static void +qlooper_handle_io_bh(void* opaque) +{ + QLooper* looper = opaque; + QLoopIo* io; + + while ((io = looper->io_pending) != NULL) { + unsigned ready; + /* extract from list */ + looper->io_pending = io->pendingNext; + io->pendingNext = NULL; + /* call the user callback, clear io->ready before to + * indicate that the item is not on the pending list + * anymore. + */ + ready = io->ready; + io->ready = 0; + io->user_callback(io->user_opaque, io->fd, ready); + } +} + +static Duration +qlooper_now(Looper* ll) +{ + return qemu_get_clock_ns(host_clock)/1000000; +} + +extern void qemu_system_shutdown_request(void); + +static void +qlooper_forceQuit(Looper* ll) +{ + qemu_system_shutdown_request(); +} + +static void +qlooper_destroy(Looper* ll) +{ + QLooper* looper = (QLooper*)ll; + QLoopIo* io; + + while ((io = looper->io_list) != NULL) + qloopio_free(io); + + qemu_bh_delete(looper->io_bh); + qemu_free(looper); +} + +Looper* +looper_newCore(void) +{ + QLooper* looper = qemu_mallocz(sizeof(*looper)); + + looper->io_list = NULL; + looper->io_pending = NULL; + looper->io_bh = qemu_bh_new(qlooper_handle_io_bh, looper); + + looper->looper.now = qlooper_now; + looper->looper.timer_init = qlooper_timer_init; + looper->looper.io_init = qlooper_io_init; + looper->looper.forceQuit = qlooper_forceQuit; + looper->looper.destroy = qlooper_destroy; + + return &looper->looper; +} diff --git a/android/looper.h b/android/looper.h new file mode 100644 index 0000000..52a9189 --- /dev/null +++ b/android/looper.h @@ -0,0 +1,296 @@ +/* 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. +*/ +#ifndef ANDROID_LOOPER_H +#define ANDROID_LOOPER_H + +#include +#include +#include + +/********************************************************************** + ********************************************************************** + ***** + ***** T I M E R E P R E S E N T A T I O N + ***** + ********************************************************************** + **********************************************************************/ + +/* An Duration represents a duration in milliseconds */ +typedef int64_t Duration; + +/* A special Duration value used to mean "infinite" */ +#define DURATION_INFINITE ((Duration)INT64_MAX) + +/********************************************************************** + ********************************************************************** + ***** + ***** E V E N T L O O P O B J E C T S + ***** + ********************************************************************** + **********************************************************************/ + + +/* A Looper is an abstraction for an event loop, which can + * be implemented in different ways. For example, the UI program may + * want to implement a custom event loop on top of the SDL event queue, + * while the QEMU core would implement it on top of QEMU's internal + * main loop which works differently. + * + * Once you have a Looper pointer, you can register "watchers" that + * will trigger callbacks whenever certain events occur. Supported event + * types are: + * + * - timer expiration + * - i/o file descriptor input/output + * + * See the relevant documentation for these below. + * + * Once you have registered one or more watchers, you can call looper_run() + * which will run the event loop until looper_forceQuit() is called from a + * callback, or no more watchers are registered. + * + * You can register/unregister watchers from a callback, or call various + * Looper methods from them (e.g. looper_now(), looper_forceQuit(), etc..) + * + * You can create a new Looper by calling looper_newGeneric(). This provides + * a default implementation that can be used in all threads. + * + * For the QEMU core, you can grab a Looper pointer by calling + * looper_newCore() instead. Its implementation relies on top of + * the QEMU event loop instead. + */ +typedef struct Looper Looper; + +/* Create a new generic looper that can be used in any context / thread. */ +Looper* looper_newGeneric(void); + +/* Create a new looper which is implemented on top of the QEMU main event + * loop. You should only use this when implementing the emulator UI and Core + * features in a single program executable. + */ +Looper* looper_newCore(void); + + +typedef struct LoopTimer LoopTimer; +typedef void (*LoopTimerFunc)(void* opaque); + +typedef struct LoopIo LoopIo; +typedef void (*LoopIoFunc)(void* opaque, int fd, unsigned events); + +struct Looper { + Duration (*now) (Looper* looper); + void (*timer_init)(Looper* looper, LoopTimer* timer, LoopTimerFunc callback, void* opaque); + void (*io_init) (Looper* looper, LoopIo* io, int fd, LoopIoFunc callback, void* opaque); + void (*run) (Looper* looper); + void (*forceQuit) (Looper* looper); + void (*destroy) (Looper* looper); +}; + + + +/********************************************************************** + ********************************************************************** + ***** + ***** T I M E R S + ***** + ********************************************************************** + **********************************************************************/ + + +typedef struct LoopTimerClass LoopTimerClass; + +struct LoopTimer { + LoopTimerClass* clazz; + void* impl; +}; + +struct LoopTimerClass { + void (*startRelative)(void* impl, Duration timeout_ms); + void (*startAbsolute)(void* impl, Duration deadline_ms); + void (*stop) (void* impl); + int (*isActive) (void* impl); + void (*done) (void* impl); +}; + +/* Initialize a LoopTimer with a callback and an 'opaque' value. + * Each timer belongs to only one looper object. + */ +extern __inline__ void +loopTimer_init(LoopTimer* timer, + Looper* looper, + LoopTimerFunc callback, + void* opaque) +{ + looper->timer_init(looper, timer, callback, opaque); +} + +/* Finalize a LoopTimer */ +extern __inline__ void +loopTimer_done(LoopTimer* timer) +{ + timer->clazz->done(timer->impl); + timer->clazz = NULL; + timer->impl = NULL; +} + +/* Start a timer, i.e. arm it to expire in 'timeout_ms' milliseconds, + * unless loopTimer_stop() is called before that, or the timer is + * reprogrammed with another loopTimer_startXXX() call. + */ +extern __inline__ void +loopTimer_startRelative(LoopTimer* timer, Duration timeout_ms) +{ + timer->clazz->startRelative(timer->impl, timeout_ms); +} + +/* A variant of loopTimer_startRelative that fires on a given deadline + * in milliseconds instead. If the deadline already passed, the timer is + * automatically appended to the list of pending event watchers and will + * fire as soon as possible. Note that this can cause infinite loops + * in your code if you're not careful. + */ +extern __inline__ void +loopTimer_startAbsolute(LoopTimer* timer, Duration deadline_ms) +{ + timer->clazz->startAbsolute(timer->impl, deadline_ms); +} + +/* Stop a given timer */ +extern __inline__ void +loopTimer_stop(LoopTimer* timer) +{ + timer->clazz->stop(timer->impl); +} + +/* Returns true iff the timer is active / started */ +extern __inline__ int +loopTimer_isActive(LoopTimer* timer) +{ + return timer->clazz->isActive(timer->impl); +} + +/********************************************************************** + ********************************************************************** + ***** + ***** F I L E D E S C R I P T O R S + ***** + ********************************************************************** + **********************************************************************/ + +typedef struct LoopIoClass LoopIoClass; + +struct LoopIo { + LoopIoClass* clazz; + void* impl; + int fd; +}; + +/* Bitmasks about i/o events. Note that errors (e.g. network disconnections) + * are mapped to both read and write events. The idea is that a read() or + * write() will return 0 or even -1 on non-blocking file descriptors in this + * case. + * + * You can receive several events at the same time on a single LoopIo + * + * Socket connect()s are mapped to LOOP_IO_WRITE events. + * Socket accept()s are mapped to LOOP_IO_READ events. + */ +enum { + LOOP_IO_READ = (1 << 0), + LOOP_IO_WRITE = (1 << 1), +}; + +struct LoopIoClass { + void (*wantRead)(void* impl); + void (*wantWrite)(void* impl); + void (*dontWantRead)(void* impl); + void (*dontWantWrite)(void* impl); + void (*done)(void* impl); +}; + +extern __inline__ void +loopIo_init(LoopIo* io, Looper* looper, int fd, LoopIoFunc callback, void* opaque) +{ + looper->io_init(looper, io, fd, callback, opaque); + io->fd = fd; +} + +/* Note: This does not close the file descriptor! */ +extern __inline__ void +loopIo_done(LoopIo* io) +{ + io->clazz->done(io->impl); +} + +/* The following functions are used to indicate whether you want the callback + * to be fired when there is data to be read or when the file is ready to + * be written to. */ +extern __inline__ void +loopIo_wantRead(LoopIo* io) +{ + io->clazz->wantRead(io->impl); +} +extern __inline__ void +loopIo_wantWrite(LoopIo* io) +{ + io->clazz->wantWrite(io->impl); +} +extern __inline__ void +loopIo_dontWantRead(LoopIo* io) +{ + io->clazz->dontWantRead(io->impl); +} +extern __inline__ void +loopIo_dontWantWrite(LoopIo* io) +{ + io->clazz->dontWantWrite(io->impl); +} + +/********************************************************************** + ********************************************************************** + ***** + ***** L O O P E R + ***** + ********************************************************************** + **********************************************************************/ + +/* Run the event loop, until looper_forceQuit() is called, or there is no + * more registered watchers for events/timers in the looper. + */ +extern __inline__ void +looper_run(Looper* looper) +{ + looper->run(looper); +} + +extern __inline__ void +looper_forceQuit(Looper* looper) +{ + looper->forceQuit(looper); +} + +/* Destroy a given looper object. Only works for those created + * with looper_new(). Cannot be called within looper_run()!! + * + * NOTE: This assumes that the user has destroyed all its + * timers and ios properly + */ +extern __inline__ void +looper_free(Looper* looper) +{ + if (looper) + looper->destroy(looper); +} + +/* */ + +#endif /* ANDROID_LOOPER_H */ diff --git a/android/utils/refset.h b/android/utils/refset.h index 12cc240..d734999 100644 --- a/android/utils/refset.h +++ b/android/utils/refset.h @@ -34,6 +34,12 @@ arefSet_done( ARefSet* s ) AVECTOR_DONE(s,buckets); } +AINLINED void +arefSet_clear( ARefSet* s ) +{ + AVECTOR_CLEAR(s,buckets); +} + AINLINED int arefSet_count( ARefSet* s ) { diff --git a/android/utils/vector.h b/android/utils/vector.h index e591055..c576ec7 100644 --- a/android/utils/vector.h +++ b/android/utils/vector.h @@ -44,6 +44,11 @@ (obj)->max_##name = 0; \ } while (0) +#define AVECTOR_CLEAR(obj,name) \ + do { \ + (obj)->num_##name = 0; \ + } while (0) + #define AVECTOR_AT(obj,name,index) \ (&(obj)->name[(index)]) diff --git a/iolooper-select.c b/iolooper-select.c index dbb18dd..dc5257c 100644 --- a/iolooper-select.c +++ b/iolooper-select.c @@ -57,6 +57,29 @@ iolooper_del_fd( IoLooper* iol, int fd ) iol->max_fd_valid = 0; } +void +iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags ) +{ + if (fd < 0) + return; + + int changed = oldflags ^ newflags; + + if ((changed & IOLOOPER_READ) != 0) { + if ((newflags & IOLOOPER_READ) != 0) + FD_SET(fd, iol->reads); + else + FD_CLR(fd, iol->reads); + } + if ((changed & IOLOOPER_WRITE) != 0) { + if ((newflags & IOLOOPER_WRITE) != 0) + FD_SET(fd, iol->writes); + else + FD_CLR(fd, iol->writes); + } +} + + static int iolooper_fd_count( IoLooper* iol ) { diff --git a/iolooper.h b/iolooper.h index f051d07..0345774 100644 --- a/iolooper.h +++ b/iolooper.h @@ -16,6 +16,12 @@ void iolooper_add_write( IoLooper* iol, int fd ); void iolooper_del_read( IoLooper* iol, int fd ); void iolooper_del_write( IoLooper* iol, int fd ); +enum { + IOLOOPER_READ = (1<<0), + IOLOOPER_WRITE = (1<<1), +}; +void iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags); + int iolooper_poll( IoLooper* iol ); /* Wrapper around select() * Return: -- cgit v1.1