ALooper.cpp revision f8be8c0c8055ead961d23b969bf46315eb93e887
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ALooper"
19#include <utils/Log.h>
20
21#include <sys/time.h>
22
23#include "ALooper.h"
24
25#include "AHandler.h"
26#include "ALooperRoster.h"
27#include "AMessage.h"
28
29namespace android {
30
31ALooperRoster gLooperRoster;
32
33struct ALooper::LooperThread : public Thread {
34    LooperThread(ALooper *looper, bool canCallJava)
35        : Thread(canCallJava),
36          mLooper(looper),
37          mThreadId(NULL) {
38    }
39
40    virtual status_t readyToRun() {
41        mThreadId = androidGetThreadId();
42
43        return Thread::readyToRun();
44    }
45
46    virtual bool threadLoop() {
47        return mLooper->loop();
48    }
49
50    bool isCurrentThread() const {
51        return mThreadId == androidGetThreadId();
52    }
53
54protected:
55    virtual ~LooperThread() {}
56
57private:
58    ALooper *mLooper;
59    android_thread_id_t mThreadId;
60
61    DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
62};
63
64// static
65int64_t ALooper::GetNowUs() {
66    struct timeval tv;
67    gettimeofday(&tv, NULL);
68
69    return (int64_t)tv.tv_sec * 1000000ll + tv.tv_usec;
70}
71
72ALooper::ALooper()
73    : mRunningLocally(false) {
74}
75
76ALooper::~ALooper() {
77    stop();
78}
79
80void ALooper::setName(const char *name) {
81    mName = name;
82}
83
84ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
85    return gLooperRoster.registerHandler(this, handler);
86}
87
88void ALooper::unregisterHandler(handler_id handlerID) {
89    gLooperRoster.unregisterHandler(handlerID);
90}
91
92status_t ALooper::start(
93        bool runOnCallingThread, bool canCallJava, int32_t priority) {
94    if (runOnCallingThread) {
95        {
96            Mutex::Autolock autoLock(mLock);
97
98            if (mThread != NULL || mRunningLocally) {
99                return INVALID_OPERATION;
100            }
101
102            mRunningLocally = true;
103        }
104
105        do {
106        } while (loop());
107
108        return OK;
109    }
110
111    Mutex::Autolock autoLock(mLock);
112
113    if (mThread != NULL || mRunningLocally) {
114        return INVALID_OPERATION;
115    }
116
117    mThread = new LooperThread(this, canCallJava);
118
119    status_t err = mThread->run(
120            mName.empty() ? "ALooper" : mName.c_str(), priority);
121    if (err != OK) {
122        mThread.clear();
123    }
124
125    return err;
126}
127
128status_t ALooper::stop() {
129    sp<LooperThread> thread;
130    bool runningLocally;
131
132    {
133        Mutex::Autolock autoLock(mLock);
134
135        thread = mThread;
136        runningLocally = mRunningLocally;
137        mThread.clear();
138        mRunningLocally = false;
139    }
140
141    if (thread == NULL && !runningLocally) {
142        return INVALID_OPERATION;
143    }
144
145    if (thread != NULL) {
146        thread->requestExit();
147    }
148
149    mQueueChangedCondition.signal();
150
151    if (!runningLocally && !thread->isCurrentThread()) {
152        // If not running locally and this thread _is_ the looper thread,
153        // the loop() function will return and never be called again.
154        thread->requestExitAndWait();
155    }
156
157    return OK;
158}
159
160void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
161    Mutex::Autolock autoLock(mLock);
162
163    int64_t whenUs;
164    if (delayUs > 0) {
165        whenUs = GetNowUs() + delayUs;
166    } else {
167        whenUs = GetNowUs();
168    }
169
170    List<Event>::iterator it = mEventQueue.begin();
171    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
172        ++it;
173    }
174
175    Event event;
176    event.mWhenUs = whenUs;
177    event.mMessage = msg;
178
179    if (it == mEventQueue.begin()) {
180        mQueueChangedCondition.signal();
181    }
182
183    mEventQueue.insert(it, event);
184}
185
186bool ALooper::loop() {
187    Event event;
188
189    {
190        Mutex::Autolock autoLock(mLock);
191        if (mThread == NULL && !mRunningLocally) {
192            return false;
193        }
194        if (mEventQueue.empty()) {
195            mQueueChangedCondition.wait(mLock);
196            return true;
197        }
198        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
199        int64_t nowUs = GetNowUs();
200
201        if (whenUs > nowUs) {
202            int64_t delayUs = whenUs - nowUs;
203            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
204
205            return true;
206        }
207
208        event = *mEventQueue.begin();
209        mEventQueue.erase(mEventQueue.begin());
210    }
211
212    gLooperRoster.deliverMessage(event.mMessage);
213
214    // NOTE: It's important to note that at this point our "ALooper" object
215    // may no longer exist (its final reference may have gone away while
216    // delivering the message). We have made sure, however, that loop()
217    // won't be called again.
218
219    return true;
220}
221
222}  // namespace android
223