aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2010-11-17 17:58:29 +0100
committerDavid 'Digit' Turner <digit@android.com>2010-11-18 09:32:28 +0100
commit7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9 (patch)
treeebfcf03154525d19357b25c8dea82c2b42fd0bad
parentf6cbbf529c7f4d3164243fc2cb9241978b954633 (diff)
downloadexternal_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
-rw-r--r--Makefile.android10
-rw-r--r--android/looper-generic.c466
-rw-r--r--android/looper-qemu.c402
-rw-r--r--android/looper.h296
-rw-r--r--android/utils/refset.h6
-rw-r--r--android/utils/vector.h5
-rw-r--r--iolooper-select.c23
-rw-r--r--iolooper.h6
8 files changed, 1211 insertions, 3 deletions
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 <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;
+}
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 <android/looper.h>
+#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 <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+
+/**********************************************************************
+ **********************************************************************
+ *****
+ ***** 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: