blob: 61bac02ad368e9f4d6327f012ab1003516ae8321 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "ALooper"
#include <media/stagefright/foundation/ADebug.h>
#include <utils/Log.h>
#include <sys/time.h>
#include "ALooper.h"
#include "AHandler.h"
#include "ALooperRoster.h"
#include "AMessage.h"
namespace android {
ALooperRoster gLooperRoster;
struct ALooper::LooperThread : public Thread {
LooperThread(ALooper *looper, bool canCallJava)
: Thread(canCallJava),
mLooper(looper),
mThreadId(NULL) {
}
virtual status_t readyToRun() {
mThreadId = androidGetThreadId();
return Thread::readyToRun();
}
virtual bool threadLoop() {
return mLooper->loop();
}
bool isCurrentThread() const {
return mThreadId == androidGetThreadId();
}
protected:
virtual ~LooperThread() {}
private:
ALooper *mLooper;
android_thread_id_t mThreadId;
DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
};
// static
int64_t ALooper::GetNowUs() {
return systemTime(SYSTEM_TIME_MONOTONIC) / 1000LL;
}
int64_t ALooper::getNowUs() {
return GetNowUs();
}
ALooper::ALooper()
: mRunningLocally(false) {
// clean up stale AHandlers. Doing it here instead of in the destructor avoids
// the side effect of objects being deleted from the unregister function recursively.
gLooperRoster.unregisterStaleHandlers();
}
ALooper::~ALooper() {
stop();
// stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
}
void ALooper::setName(const char *name) {
mName = name;
}
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
return gLooperRoster.registerHandler(this, handler);
}
void ALooper::unregisterHandler(handler_id handlerID) {
gLooperRoster.unregisterHandler(handlerID);
}
status_t ALooper::start(
bool runOnCallingThread, bool canCallJava, int32_t priority) {
if (runOnCallingThread) {
{
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
}
mRunningLocally = true;
}
do {
} while (loop());
return OK;
}
Mutex::Autolock autoLock(mLock);
if (mThread != NULL || mRunningLocally) {
return INVALID_OPERATION;
}
mThread = new LooperThread(this, canCallJava);
status_t err = mThread->run(
mName.empty() ? "ALooper" : mName.c_str(), priority);
if (err != OK) {
mThread.clear();
}
return err;
}
status_t ALooper::stop() {
sp<LooperThread> thread;
bool runningLocally;
{
Mutex::Autolock autoLock(mLock);
thread = mThread;
runningLocally = mRunningLocally;
mThread.clear();
mRunningLocally = false;
}
if (thread == NULL && !runningLocally) {
return INVALID_OPERATION;
}
if (thread != NULL) {
thread->requestExit();
}
mQueueChangedCondition.signal();
{
Mutex::Autolock autoLock(mRepliesLock);
mRepliesCondition.broadcast();
}
if (!runningLocally && !thread->isCurrentThread()) {
// If not running locally and this thread _is_ the looper thread,
// the loop() function will return and never be called again.
thread->requestExitAndWait();
}
return OK;
}
void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
Mutex::Autolock autoLock(mLock);
int64_t whenUs;
if (delayUs > 0) {
int64_t nowUs = getNowUs();
whenUs = (delayUs > INT64_MAX - nowUs ? INT64_MAX : nowUs + delayUs);
} else {
whenUs = getNowUs();
}
List<Event>::iterator it = mEventQueue.begin();
while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
++it;
}
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
event.mToken = nullptr;
if (it == mEventQueue.begin()) {
mQueueChangedCondition.signal();
}
mEventQueue.insert(it, event);
}
status_t ALooper::postUnique(const sp<AMessage> &msg, const sp<RefBase> &token, int64_t delayUs) {
if (token == nullptr) {
return -EINVAL;
}
Mutex::Autolock autoLock(mLock);
int64_t whenUs;
if (delayUs > 0) {
int64_t nowUs = getNowUs();
whenUs = (delayUs > INT64_MAX - nowUs ? INT64_MAX : nowUs + delayUs);
} else {
whenUs = getNowUs();
}
// We only need to wake the loop up if we're rescheduling to the earliest event in the queue.
// This needs to be checked now, before we reschedule the message, in case this message is
// already at the beginning of the queue.
bool shouldAwakeLoop = mEventQueue.empty() || whenUs < mEventQueue.begin()->mWhenUs;
// Erase any previously-posted event with this token.
for (auto i = mEventQueue.begin(); i != mEventQueue.end();) {
if (i->mToken == token) {
i = mEventQueue.erase(i);
} else {
++i;
}
}
// Find the insertion point for the rescheduled message.
List<Event>::iterator i = mEventQueue.begin();
while (i != mEventQueue.end() && i->mWhenUs <= whenUs) {
++i;
}
Event event;
event.mWhenUs = whenUs;
event.mMessage = msg;
event.mToken = token;
mEventQueue.insert(i, event);
// If we rescheduled the event to be earlier than the first event, then we need to wake up the
// looper earlier than it was previously scheduled to be woken up. Otherwise, it can sleep until
// the previous wake-up time and then go to sleep again if needed.
if (shouldAwakeLoop){
mQueueChangedCondition.signal();
}
return OK;
}
bool ALooper::loop() {
Event event;
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL && !mRunningLocally) {
return false;
}
if (mEventQueue.empty()) {
mQueueChangedCondition.wait(mLock);
return true;
}
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
int64_t nowUs = getNowUs();
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
if (delayUs > INT64_MAX / 1000) {
delayUs = INT64_MAX / 1000;
}
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
event = *mEventQueue.begin();
mEventQueue.erase(mEventQueue.begin());
}
event.mMessage->deliver();
// NOTE: It's important to note that at this point our "ALooper" object
// may no longer exist (its final reference may have gone away while
// delivering the message). We have made sure, however, that loop()
// won't be called again.
return true;
}
// to be called by AMessage::postAndAwaitResponse only
sp<AReplyToken> ALooper::createReplyToken() {
return new AReplyToken(this);
}
// to be called by AMessage::postAndAwaitResponse only
status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
// return status in case we want to handle an interrupted wait
Mutex::Autolock autoLock(mRepliesLock);
CHECK(replyToken != NULL);
while (!replyToken->retrieveReply(response)) {
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL) {
return -ENOENT;
}
}
mRepliesCondition.wait(mRepliesLock);
}
return OK;
}
status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
Mutex::Autolock autoLock(mRepliesLock);
status_t err = replyToken->setReply(reply);
if (err == OK) {
mRepliesCondition.broadcast();
}
return err;
}
} // namespace android