aboutsummaryrefslogtreecommitdiffstats
path: root/android/looper-qemu.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/looper-qemu.c')
-rw-r--r--android/looper-qemu.c402
1 files changed, 402 insertions, 0 deletions
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;
+}