blob: 873459839ebacb7dd53efe8345547c7af2f8ff38 [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/base/async/DefaultLooper.h"
#include "android/base/Log.h"
#include "android/base/system/System.h"
#include "android/base/sockets/SocketErrors.h"
namespace android {
namespace base {
DefaultLooper::DefaultLooper() : mWaiter(SocketWaiter::create()) {}
DefaultLooper::~DefaultLooper() = default;
Looper::Duration DefaultLooper::nowMs(Looper::ClockType clockType) {
DCHECK(clockType == ClockType::kHost);
const DurationNs nsTime = nowNs(clockType);
return nsTime == static_cast<DurationNs>(-1)
? -1
: static_cast<Duration>(nsTime / 1000000);
}
Looper::DurationNs DefaultLooper::nowNs(Looper::ClockType clockType) {
DCHECK(clockType == ClockType::kHost);
return System::get()->getUnixTimeUs() * 1000ull;
}
void DefaultLooper::forceQuit() {
mForcedExit = true;
}
void DefaultLooper::addFdWatch(DefaultLooper::FdWatch* watch) {
mFdWatches.add(watch);
}
void DefaultLooper::delFdWatch(DefaultLooper::FdWatch* watch) {
mFdWatches.pick(watch);
}
void DefaultLooper::addPendingFdWatch(DefaultLooper::FdWatch* watch) {
mPendingFdWatches.insertTail(watch);
}
void DefaultLooper::delPendingFdWatch(DefaultLooper::FdWatch* watch) {
mPendingFdWatches.remove(watch);
}
void DefaultLooper::updateFdWatch(int fd, unsigned wantedEvents) {
mWaiter->update(fd, wantedEvents);
}
Looper::FdWatch* DefaultLooper::createFdWatch(int fd,
Looper::FdWatch::Callback callback,
void* opaque) {
return new FdWatch(this, fd, callback, opaque);
}
void DefaultLooper::addTimer(DefaultLooper::Timer* timer) {
mTimers.add(timer);
}
void DefaultLooper::delTimer(DefaultLooper::Timer* timer) {
mTimers.pick(timer);
}
void DefaultLooper::enableTimer(DefaultLooper::Timer* timer) {
// Find the first timer that expires after this one.
Timer* item = mActiveTimers.front();
while (item && item->deadline() < timer->deadline()) {
item = item->next();
}
if (item) {
mActiveTimers.insertBefore(item, timer);
} else {
mActiveTimers.insertTail(timer);
}
}
void DefaultLooper::disableTimer(DefaultLooper::Timer* timer) {
// Simply remove from the list.
mActiveTimers.remove(timer);
}
void DefaultLooper::addPendingTimer(DefaultLooper::Timer* timer) {
mPendingTimers.insertTail(timer);
}
void DefaultLooper::delPendingTimer(DefaultLooper::Timer* timer) {
mPendingTimers.remove(timer);
}
Looper::Timer* DefaultLooper::createTimer(Looper::Timer::Callback callback,
void* opaque,
Looper::ClockType clock) {
return new DefaultLooper::Timer(this, callback, opaque, clock);
}
int DefaultLooper::runWithDeadlineMs(Looper::Duration deadlineMs) {
mForcedExit = false;
while (!mForcedExit) {
// Return immediately with EWOULDBLOCK if there are no
// more timers or watches registered.
if (mFdWatches.empty() && mActiveTimers.empty()) {
return EWOULDBLOCK;
}
// Return immediately with ETIMEDOUT if we've overrun the deadline.
// This can happen in two ways:
// - We waited till timeout on some FDs.
// - We have no FDs to watch, but some timers overran the looper
// deadline.
if (nowMs() > deadlineMs) {
return ETIMEDOUT;
}
if (!runOneIterationWithDeadlineMs(deadlineMs)) {
break;
}
}
return 0;
}
bool DefaultLooper::runOneIterationWithDeadlineMs(Looper::Duration deadlineMs) {
// Compute next deadline from timers.
Duration nextDeadline = kDurationInfinite;
Timer* firstTimer = mActiveTimers.front();
if (firstTimer) {
nextDeadline = firstTimer->deadline();
}
if (nextDeadline > deadlineMs) {
nextDeadline = deadlineMs;
}
// Wait for fd events until next deadline.
Duration timeOut = INT64_MAX;
if (nextDeadline < kDurationInfinite) {
timeOut = nextDeadline - nowMs();
if (timeOut < 0)
timeOut = 0;
}
if (!mFdWatches.empty()) {
int ret = mWaiter->wait(timeOut);
if (ret < 0) {
// Error, force stop.
return false;
}
DCHECK(mPendingFdWatches.empty());
if (ret > 0) {
// Queue pending FdWatch instances.
for (;;) {
unsigned events;
int fd = mWaiter->nextPendingFd(&events);
if (fd < 0) {
break;
}
// Find the FdWatch for this file descriptor.
// TODO(digit): Improve efficiency with a map?
FdWatchSet::Iterator iter(&mFdWatches);
while (iter.hasNext()) {
FdWatch* watch = iter.next();
if (watch->fd() == fd) {
watch->setPending(events);
break;
}
}
}
}
} else {
// We don't have any FDs to watch, so just sleep till the next
// timer expires.
System::get()->sleepMs(timeOut);
}
// Queue pending expired timers.
DCHECK(mPendingTimers.empty());
const Duration kNow = nowMs();
Timer* timer = mActiveTimers.front();
while (timer) {
if (timer->deadline() > kNow) {
break;
}
// Remove from active list, add to pending list.
mActiveTimers.remove(timer);
timer->setPending();
timer = timer->next();
}
// Fire the pending timers, this is done in a separate step
// because the callback could modify the active timers list.
for (;;) {
Timer* timer = mPendingTimers.front();
if (!timer) {
break;
}
timer->clearPending();
timer->fire();
}
// Fire the pending fd watches. Also done in a separate step
// since the callbacks could modify
for (;;) {
FdWatch* watch = mPendingFdWatches.front();
if (!watch) {
break;
}
watch->clearPending();
watch->fire();
}
return true;
}
DefaultLooper::FdWatch::FdWatch(DefaultLooper* looper,
int fd,
Looper::FdWatch::Callback callback,
void* opaque)
: Looper::FdWatch(looper, fd, callback, opaque),
mWantedEvents(0U),
mLastEvents(0U),
mPending(false),
mPendingLink() {
looper->addFdWatch(this);
}
DefaultLooper* DefaultLooper::FdWatch::defaultLooper() const {
return reinterpret_cast<DefaultLooper*>(mLooper);
}
DefaultLooper::FdWatch::~FdWatch() {
dontWantWrite();
dontWantRead();
clearPending();
defaultLooper()->delFdWatch(this);
}
void DefaultLooper::FdWatch::addEvents(unsigned events) {
events &= kEventMask;
unsigned newEvents = mWantedEvents | events;
if (newEvents != mWantedEvents) {
mWantedEvents = newEvents;
defaultLooper()->updateFdWatch(mFd, newEvents);
}
}
void DefaultLooper::FdWatch::removeEvents(unsigned events) {
events &= kEventMask;
unsigned newEvents = mWantedEvents & ~events;
if (newEvents != mWantedEvents) {
mWantedEvents = newEvents;
defaultLooper()->updateFdWatch(mFd, newEvents);
if (!newEvents) {
clearPending();
}
}
// These events are no longer desired.
mLastEvents &= ~events;
}
unsigned DefaultLooper::FdWatch::poll() const {
return mLastEvents;
}
bool DefaultLooper::FdWatch::isPending() const {
return mPending;
}
void DefaultLooper::FdWatch::setPending(unsigned events) {
DCHECK(!mPending);
mPending = true;
mLastEvents = events & kEventMask;
defaultLooper()->addPendingFdWatch(this);
}
void DefaultLooper::FdWatch::clearPending() {
if (mPending) {
defaultLooper()->delPendingFdWatch(this);
mPending = false;
}
}
void DefaultLooper::FdWatch::fire() {
unsigned events = mLastEvents;
mLastEvents = 0;
mCallback(mOpaque, mFd, events);
}
DefaultLooper::Timer::Timer(DefaultLooper* looper,
Looper::Timer::Callback callback,
void* opaque,
Looper::ClockType clock)
: Looper::Timer(looper, callback, opaque, clock),
mDeadline(kDurationInfinite),
mPending(false),
mPendingLink() {
// this implementation only supports a host clock
DCHECK(clock == ClockType::kHost);
DCHECK(mCallback);
looper->addTimer(this);
}
DefaultLooper* DefaultLooper::Timer::defaultLooper() const {
return reinterpret_cast<DefaultLooper*>(mLooper);
}
DefaultLooper::Timer::~Timer() {
clearPending();
defaultLooper()->delTimer(this);
}
DefaultLooper::Timer* DefaultLooper::Timer::next() const {
return mPendingLink.next();
}
Looper::Duration DefaultLooper::Timer::deadline() const {
return mDeadline;
}
void DefaultLooper::Timer::startRelative(Looper::Duration deadlineMs) {
if (deadlineMs != kDurationInfinite) {
deadlineMs += mLooper->nowMs(mClockType);
}
startAbsolute(deadlineMs);
}
void DefaultLooper::Timer::startAbsolute(Looper::Duration deadlineMs) {
if (mDeadline != kDurationInfinite) {
defaultLooper()->disableTimer(this);
}
mDeadline = deadlineMs;
if (mDeadline != kDurationInfinite) {
defaultLooper()->enableTimer(this);
}
}
void DefaultLooper::Timer::stop() {
startAbsolute(kDurationInfinite);
}
bool DefaultLooper::Timer::isActive() const {
return mDeadline != kDurationInfinite;
}
void DefaultLooper::Timer::setPending() {
mPending = true;
defaultLooper()->addPendingTimer(this);
}
void DefaultLooper::Timer::clearPending() {
if (mPending) {
defaultLooper()->delPendingTimer(this);
mPending = false;
// resets mDeadline so it can be added to active timers list again later
// without assertion failure inside startAbsolute
// (defaultLooper->disableTimer)
mDeadline = kDurationInfinite;
}
}
void DefaultLooper::Timer::fire() {
mCallback(mOpaque, this);
}
void DefaultLooper::Timer::save(Stream* stream) const {
stream->putBe64(static_cast<uint64_t>(mDeadline));
}
void DefaultLooper::Timer::load(Stream* stream) {
const Duration deadline = static_cast<Duration>(stream->getBe64());
startAbsolute(deadline);
}
} // namespace base
} // namespace android