aboutsummaryrefslogtreecommitdiffstats
path: root/android/looper.h
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 /android/looper.h
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
Diffstat (limited to 'android/looper.h')
-rw-r--r--android/looper.h296
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 */