/* 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 #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_ms(host_clock) + timeout_ms); } 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); } 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_ms(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); io->wanted = wanted; } 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 unsigned qloopio_poll(void* impl) { QLoopIo* io = impl; return io->ready; } static const LoopIoClass qlooper_io_class = { qloopio_wantRead, qloopio_wantWrite, qloopio_dontWantRead, qloopio_dontWantWrite, qloopio_poll, 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; } 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_ms(host_clock); } extern void qemu_system_shutdown_request(void); static void qlooper_forceQuit(Looper* ll) { qemu_system_shutdown_request(); } /* The user cannot call looper_run on the core event loop, because it * is started by qemu_main() explicitely instead, so just panic. */ int qlooper_run(Looper* ll, Duration deadline_ms) { APANIC("Trying to run the QEMU main event loop explicitely!"); return EINVAL; } 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.run = qlooper_run; looper->looper.forceQuit = qlooper_forceQuit; looper->looper.destroy = qlooper_destroy; return &looper->looper; }