diff options
Diffstat (limited to 'android/looper-qemu.c')
-rw-r--r-- | android/looper-qemu.c | 402 |
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; +} |