diff options
author | David 'Digit' Turner <digit@android.com> | 2010-11-17 17:58:29 +0100 |
---|---|---|
committer | David 'Digit' Turner <digit@android.com> | 2010-11-18 09:32:28 +0100 |
commit | 7a17b608de24e3aaf7d5ca030bb80a74dcc3baf9 (patch) | |
tree | ebfcf03154525d19357b25c8dea82c2b42fd0bad /android/looper.h | |
parent | f6cbbf529c7f4d3164243fc2cb9241978b954633 (diff) | |
download | external_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
Diffstat (limited to 'android/looper.h')
-rw-r--r-- | android/looper.h | 296 |
1 files changed, 296 insertions, 0 deletions
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 */ |