| // 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 |
| |