322 lines
8.2 KiB
C++
322 lines
8.2 KiB
C++
/*
|
|
* 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
|