blob: 643047f59aab8d67c5663ee02471d390fdd1e115 [file] [log] [blame]
// 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