aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2016-09-14 14:55:57 +0200
committerDavid 'Digit' Turner <digit@google.com>2016-10-18 15:44:24 +0200
commit594b225720a75e30a099ee53953ce67d1d3d0782 (patch)
tree32327784311ae71965ec013dcbb6ef800fbbbb03
parent1b1f9139e98627ad48a4ddeb0551950f9b02dbb6 (diff)
downloadqemu-android-594b225720a75e30a099ee53953ce67d1d3d0782.tar.gz
android: glue: Add QEMU2 Looper support.
This patch ensures that the android::base::Looper instance is set correctly for any QEMU2 thread. Change-Id: I46e6f1b2a4bf121f760f9239978aea61d8c631ae
-rw-r--r--android-qemu2-glue/base/async/Looper.cpp341
-rw-r--r--android-qemu2-glue/base/async/Looper.h27
-rw-r--r--android-qemu2-glue/build/Makefile.qemu2-glue.mk2
-rw-r--r--android-qemu2-glue/looper-qemu.cpp23
-rw-r--r--android-qemu2-glue/looper-qemu.h26
-rw-r--r--android-qemu2-glue/qemu-setup.cpp7
6 files changed, 426 insertions, 0 deletions
diff --git a/android-qemu2-glue/base/async/Looper.cpp b/android-qemu2-glue/base/async/Looper.cpp
new file mode 100644
index 0000000000..643047f59a
--- /dev/null
+++ b/android-qemu2-glue/base/async/Looper.cpp
@@ -0,0 +1,341 @@
+// Copyright 2014 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.
+
+#include "android-qemu2-glue/base/async/Looper.h"
+
+#include "android/base/Log.h"
+#include "android/base/containers/TailQueueList.h"
+#include "android/base/containers/ScopedPointerSet.h"
+#include "android/base/sockets/SocketUtils.h"
+#include "android-qemu2-glue/base/files/QemuFileStream.h"
+#include "android/utils/stream.h"
+
+extern "C" {
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/char.h"
+} // extern "C"
+
+namespace android {
+namespace qemu {
+
+namespace {
+
+extern "C" void qemu_system_shutdown_request(void);
+
+typedef ::android::base::Looper BaseLooper;
+typedef ::android::base::Looper::Timer BaseTimer;
+typedef ::android::base::Looper::FdWatch BaseFdWatch;
+
+// An implementation of android::base::Looper on top of the QEMU main
+// event loop. There are few important things here:
+//
+// 1/ There is a single global QEMU event loop, so all instances returned
+// by createLooper() will really use the same state!
+//
+// In other words, don't call it more than once!
+//
+// 2/ It is not possible to call the runWithDeadlineMs() method, since
+// the event loop is started in the application's main thread by
+// the executable.
+//
+// The implementation uses a bottom-half handler to process pending
+// FdWatch instances, see the comment in the declaration of FdWatch
+// below to understand why.
+//
+class QemuLooper : public BaseLooper {
+public:
+ QemuLooper() :
+ Looper(),
+ mQemuBh(NULL),
+ mPendingFdWatches(),
+ mTimers() {
+ }
+
+ virtual ~QemuLooper() {
+ if (mQemuBh) {
+ qemu_bh_delete(mQemuBh);
+ }
+ }
+
+ static QEMUClockType toQemuClockType(ClockType clock) {
+ static_assert((int) QEMU_CLOCK_HOST == (int) BaseLooper::ClockType::kHost &&
+ (int) QEMU_CLOCK_VIRTUAL == (int) BaseLooper::ClockType::kVirtual &&
+ (int) QEMU_CLOCK_REALTIME == (int) BaseLooper::ClockType::kRealtime,
+ "Values in the Looper::ClockType enumeration are out of sync with "
+ "QEMUClockType");
+
+ return static_cast<QEMUClockType>(clock);
+ }
+
+ //
+ // F D W A T C H E S
+ //
+
+ // Implementation is slightly more complicated because QEMU uses
+ // distinct callback functions for read/write events, so use a pending
+ // list to collect all FdWatch instances that have received any of such
+ // call, then later process it.
+
+ class FdWatch : public BaseFdWatch {
+ public:
+ FdWatch(QemuLooper* looper,
+ int fd,
+ BaseFdWatch::Callback callback,
+ void* opaque) :
+ BaseFdWatch(looper, fd, callback, opaque),
+ mWantedEvents(0U),
+ mPendingEvents(0U),
+ mLink() {
+ }
+
+ virtual ~FdWatch() {
+ clearPending();
+ qemu_set_fd_handler(mFd, NULL, NULL, NULL);
+ }
+
+ virtual void addEvents(unsigned events) {
+ events &= kEventMask;
+ updateEvents(mWantedEvents | events);
+ }
+
+ virtual void removeEvents(unsigned events) {
+ events &= kEventMask;
+ updateEvents(mWantedEvents & ~events);
+ }
+
+ virtual unsigned poll() const {
+ return mPendingEvents;
+ }
+
+ bool isPending() const {
+ return mPendingEvents != 0U;
+ }
+
+ void fire() {
+ DCHECK(mPendingEvents);
+ unsigned events = mPendingEvents;
+ mPendingEvents = 0U;
+ mCallback(mOpaque, mFd, events);
+ }
+
+ TAIL_QUEUE_LIST_TRAITS(Traits, FdWatch, mLink);
+
+ private:
+ void updateEvents(unsigned events) {
+ IOHandler* cbRead = (events & kEventRead) ? handleRead : NULL;
+ IOHandler* cbWrite = (events & kEventWrite) ? handleWrite : NULL;
+ qemu_set_fd_handler(mFd, cbRead, cbWrite, this);
+ mWantedEvents = events;
+ }
+
+ void setPending(unsigned event) {
+ if (!mPendingEvents) {
+ asQemuLooper(mLooper)->addPendingFdWatch(this);
+ }
+ mPendingEvents |= event;
+ }
+
+ void clearPending() {
+ if (mPendingEvents) {
+ asQemuLooper(mLooper)->delPendingFdWatch(this);
+ mPendingEvents = 0;
+ }
+ }
+
+ // Called by QEMU on a read i/o event. |opaque| is a FdWatch handle.
+ static void handleRead(void* opaque) {
+ FdWatch* watch = static_cast<FdWatch*>(opaque);
+ watch->setPending(kEventRead);
+ }
+
+ // Called by QEMU on a write i/o event. |opaque| is a FdWatch handle.
+ static void handleWrite(void* opaque) {
+ FdWatch* watch = static_cast<FdWatch*>(opaque);
+ watch->setPending(kEventWrite);
+ }
+
+ unsigned mWantedEvents;
+ unsigned mPendingEvents;
+ ::android::base::TailQueueLink<FdWatch> mLink;
+ };
+
+ virtual BaseFdWatch* createFdWatch(int fd,
+ BaseFdWatch::Callback callback,
+ void* opaque) {
+ ::android::base::socketSetNonBlocking(fd);
+ return new FdWatch(this, fd, callback, opaque);
+ }
+
+ //
+ // T I M E R S
+ //
+ class Timer : public BaseTimer {
+ public:
+ Timer(QemuLooper* looper,
+ BaseTimer::Callback callback,
+ void* opaque, ClockType clock) :
+ BaseTimer(looper, callback, opaque, clock),
+ mTimer(NULL) {
+ mTimer = ::timer_new(QemuLooper::toQemuClockType(mClockType),
+ SCALE_MS,
+ qemuTimerCallbackAdapter,
+ this);
+ }
+
+ ~Timer() {
+ ::timer_free(mTimer);
+ }
+
+ virtual void startRelative(Duration timeout_ms) {
+ if (timeout_ms == kDurationInfinite) {
+ timer_del(mTimer);
+ } else {
+ timeout_ms += qemu_clock_get_ms(
+ QemuLooper::toQemuClockType(mClockType));
+ timer_mod(mTimer, timeout_ms);
+ }
+ }
+
+ virtual void startAbsolute(Duration deadline_ms) {
+ if (deadline_ms == kDurationInfinite) {
+ timer_del(mTimer);
+ } else {
+ timer_mod(mTimer, deadline_ms);
+ }
+ }
+
+ virtual void stop() {
+ ::timer_del(mTimer);
+ }
+
+ virtual bool isActive() const {
+ return timer_pending(mTimer);
+ }
+
+ void save(android::base::Stream* stream) const {
+ timer_put(
+ reinterpret_cast<android::qemu::QemuFileStream*>(stream)->file(),
+ mTimer);
+ }
+
+ void load(android::base::Stream* stream) {
+ timer_get(
+ reinterpret_cast<android::qemu::QemuFileStream*>(stream)->file(),
+ mTimer);
+ }
+
+ private:
+ static void qemuTimerCallbackAdapter(void* opaque) {
+ Timer* timer = static_cast<Timer*>(opaque);
+ timer->mCallback(timer->mOpaque, timer);
+ }
+
+ QEMUTimer* mTimer;
+ };
+
+ virtual BaseTimer* createTimer(BaseTimer::Callback callback,
+ void* opaque, ClockType clock) {
+ return new QemuLooper::Timer(this, callback, opaque, clock);
+ }
+
+ //
+ // L O O P E R
+ //
+
+ virtual Duration nowMs(ClockType clockType) {
+ return qemu_clock_get_ms(toQemuClockType(clockType));
+ }
+
+ virtual DurationNs nowNs(ClockType clockType) {
+ return qemu_clock_get_ns(toQemuClockType(clockType));
+ }
+
+ virtual int runWithDeadlineMs(Duration deadline_ms) {
+ CHECK(false) << "User cannot call looper_run on a QEMU event loop";
+ errno = ENOSYS;
+ return -1;
+ }
+
+ virtual void forceQuit() {
+ qemu_system_shutdown_request();
+ }
+
+private:
+
+ typedef ::android::base::TailQueueList<QemuLooper::FdWatch> FdWatchList;
+ typedef ::android::base::ScopedPointerSet<FdWatch> FdWatchSet;
+ typedef ::android::base::ScopedPointerSet<Timer> TimerSet;
+
+ static inline QemuLooper* asQemuLooper(BaseLooper* looper) {
+ return reinterpret_cast<QemuLooper*>(looper);
+ }
+
+
+ void addTimer(Timer* timer) {
+ mTimers.add(timer);
+ }
+
+ void delTimer(Timer* timer) {
+ mTimers.remove(timer);
+ }
+
+ void addPendingFdWatch(FdWatch* watch) {
+ DCHECK(watch);
+ DCHECK(!watch->isPending());
+
+ if (mPendingFdWatches.empty()) {
+ // Ensure the bottom-half is triggered to act on pending
+ // watches as soon as possible.
+ if (!mQemuBh) {
+ mQemuBh = qemu_bh_new(handleBottomHalf, this);
+ }
+ qemu_bh_schedule(mQemuBh);
+ }
+
+ mPendingFdWatches.insertTail(watch);
+ }
+
+ void delPendingFdWatch(FdWatch* watch) {
+ DCHECK(watch);
+ DCHECK(watch->isPending());
+ mPendingFdWatches.remove(watch);
+ }
+
+ // Called by QEMU as soon as the main loop has finished processed
+ // I/O events. Used to look at pending watches and fire them.
+ static void handleBottomHalf(void* opaque) {
+ QemuLooper* looper = reinterpret_cast<QemuLooper*>(opaque);
+ for (;;) {
+ FdWatch* watch = looper->mPendingFdWatches.front();
+ if (!watch) {
+ break;
+ }
+ looper->delPendingFdWatch(watch);
+ watch->fire();
+ }
+ }
+
+ QEMUBH* mQemuBh;
+ FdWatchList mPendingFdWatches;
+ TimerSet mTimers;
+};
+
+} // namespace
+
+BaseLooper* createLooper() {
+ return new QemuLooper();
+}
+
+} // namespace qemu
+} // namespace android
+
diff --git a/android-qemu2-glue/base/async/Looper.h b/android-qemu2-glue/base/async/Looper.h
new file mode 100644
index 0000000000..970638bf44
--- /dev/null
+++ b/android-qemu2-glue/base/async/Looper.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+#pragma once
+
+#include "android/base/async/Looper.h"
+
+// An implementation of android::base::Looper based on the QEMU event loop.
+namespace android {
+namespace qemu {
+
+// Create a new android::base::Looper instance that is implemented through
+// the QEMU main event loop. There is only one instance, so any call will
+// return an object corresponding to the same global state, even if they
+// are different instances!
+android::base::Looper* createLooper();
+
+} // namespace qemu
+} // namespace android
diff --git a/android-qemu2-glue/build/Makefile.qemu2-glue.mk b/android-qemu2-glue/build/Makefile.qemu2-glue.mk
index 7b9c733f46..4e6a731a44 100644
--- a/android-qemu2-glue/build/Makefile.qemu2-glue.mk
+++ b/android-qemu2-glue/build/Makefile.qemu2-glue.mk
@@ -11,8 +11,10 @@ LOCAL_C_INCLUDES += \
$(QEMU2_GLUE_INCLUDES) \
LOCAL_SRC_FILES := \
+ base/async/Looper.cpp \
base/files/QemuFileStream.cpp \
emulation/VmLock.cpp \
+ looper-qemu.cpp \
qemu-setup.cpp \
utils/stream.cpp \
diff --git a/android-qemu2-glue/looper-qemu.cpp b/android-qemu2-glue/looper-qemu.cpp
new file mode 100644
index 0000000000..e9904361e3
--- /dev/null
+++ b/android-qemu2-glue/looper-qemu.cpp
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+#include "android-qemu2-glue/looper-qemu.h"
+
+#include "android-qemu2-glue/base/async/Looper.h"
+#include "android/base/async/Looper.h"
+#include "android/utils/looper.h"
+
+typedef ::Looper CLooper;
+
+void qemu_looper_setForThread() {
+ looper_setForThreadToOwn(
+ reinterpret_cast<CLooper*>(::android::qemu::createLooper()));
+}
diff --git a/android-qemu2-glue/looper-qemu.h b/android-qemu2-glue/looper-qemu.h
new file mode 100644
index 0000000000..9b68629004
--- /dev/null
+++ b/android-qemu2-glue/looper-qemu.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2015 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.
+*/
+
+#pragma once
+
+#include "android/utils/compiler.h"
+#include "android/utils/looper.h"
+
+ANDROID_BEGIN_HEADER
+
+/* 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.
+ */
+void qemu_looper_setForThread(void);
+
+ANDROID_END_HEADER
diff --git a/android-qemu2-glue/qemu-setup.cpp b/android-qemu2-glue/qemu-setup.cpp
index 296e92a843..9daf8f5156 100644
--- a/android-qemu2-glue/qemu-setup.cpp
+++ b/android-qemu2-glue/qemu-setup.cpp
@@ -17,16 +17,23 @@
#include "android/android.h"
#include "android/base/Log.h"
#include "android-qemu2-glue/emulation/VmLock.h"
+#include "android-qemu2-glue/looper-qemu.h"
extern "C" {
#include "qemu/osdep.h"
#include "qemu-common.h"
+#include "qemu/thread.h"
} // extern "C"
using android::VmLock;
bool qemu_android_emulation_early_setup() {
+ // Ensure that the looper is set for the main thread and for any
+ // future thread created by QEMU.
+ qemu_looper_setForThread();
+ qemu_thread_register_setup_callback(qemu_looper_setForThread);
+ // Ensure the VmLock implementation is setup.
VmLock* vmLock = new qemu2::VmLock();
VmLock* prevVmLock = VmLock::set(vmLock);
CHECK(prevVmLock == nullptr) << "Another VmLock was already installed!";