TimedEventQueue.cpp revision 17e8ad9c4b8fbdebec4559702b82114fb6543b67
1/*
2 * Copyright (C) 2009 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#undef __STRICT_ANSI__
18#define __STDINT_LIMITS
19#define __STDC_LIMIT_MACROS
20#include <stdint.h>
21
22//#define LOG_NDEBUG 0
23#define LOG_TAG "TimedEventQueue"
24#include <utils/Log.h>
25#include <utils/threads.h>
26
27#include "include/TimedEventQueue.h"
28
29#include <sys/prctl.h>
30#include <sys/time.h>
31#include <sys/resource.h>
32
33#include <media/stagefright/MediaDebug.h>
34
35#ifdef ANDROID_SIMULATOR
36#include <jni.h>
37#endif
38
39namespace android {
40
41TimedEventQueue::TimedEventQueue()
42    : mNextEventID(1),
43      mRunning(false),
44      mStopped(false) {
45}
46
47TimedEventQueue::~TimedEventQueue() {
48    stop();
49}
50
51void TimedEventQueue::start() {
52    if (mRunning) {
53        return;
54    }
55
56    mStopped = false;
57
58    pthread_attr_t attr;
59    pthread_attr_init(&attr);
60    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
61
62    pthread_create(&mThread, &attr, ThreadWrapper, this);
63
64    pthread_attr_destroy(&attr);
65
66    mRunning = true;
67}
68
69void TimedEventQueue::stop(bool flush) {
70    if (!mRunning) {
71        return;
72    }
73
74    if (flush) {
75        postEventToBack(new StopEvent);
76    } else {
77        postTimedEvent(new StopEvent, INT64_MIN);
78    }
79
80    void *dummy;
81    pthread_join(mThread, &dummy);
82
83    mQueue.clear();
84
85    mRunning = false;
86}
87
88TimedEventQueue::event_id TimedEventQueue::postEvent(const sp<Event> &event) {
89    // Reserve an earlier timeslot an INT64_MIN to be able to post
90    // the StopEvent to the absolute head of the queue.
91    return postTimedEvent(event, INT64_MIN + 1);
92}
93
94TimedEventQueue::event_id TimedEventQueue::postEventToBack(
95        const sp<Event> &event) {
96    return postTimedEvent(event, INT64_MAX);
97}
98
99TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(
100        const sp<Event> &event, int64_t delay_us) {
101    CHECK(delay_us >= 0);
102    return postTimedEvent(event, getRealTimeUs() + delay_us);
103}
104
105TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
106        const sp<Event> &event, int64_t realtime_us) {
107    Mutex::Autolock autoLock(mLock);
108
109    event->setEventID(mNextEventID++);
110
111    List<QueueItem>::iterator it = mQueue.begin();
112    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
113        ++it;
114    }
115
116    QueueItem item;
117    item.event = event;
118    item.realtime_us = realtime_us;
119
120    if (it == mQueue.begin()) {
121        mQueueHeadChangedCondition.signal();
122    }
123
124    mQueue.insert(it, item);
125
126    mQueueNotEmptyCondition.signal();
127
128    return event->eventID();
129}
130
131static bool MatchesEventID(
132        void *cookie, const sp<TimedEventQueue::Event> &event) {
133    TimedEventQueue::event_id *id =
134        static_cast<TimedEventQueue::event_id *>(cookie);
135
136    if (event->eventID() != *id) {
137        return false;
138    }
139
140    *id = 0;
141
142    return true;
143}
144
145bool TimedEventQueue::cancelEvent(event_id id) {
146    if (id == 0) {
147        return false;
148    }
149
150    cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
151
152    // if MatchesEventID found a match, it will have set id to 0
153    // (which is not a valid event_id).
154
155    return id == 0;
156}
157
158void TimedEventQueue::cancelEvents(
159        bool (*predicate)(void *cookie, const sp<Event> &event),
160        void *cookie,
161        bool stopAfterFirstMatch) {
162    Mutex::Autolock autoLock(mLock);
163
164    List<QueueItem>::iterator it = mQueue.begin();
165    while (it != mQueue.end()) {
166        if (!(*predicate)(cookie, (*it).event)) {
167            ++it;
168            continue;
169        }
170
171        if (it == mQueue.begin()) {
172            mQueueHeadChangedCondition.signal();
173        }
174
175        LOGV("cancelling event %d", (*it).event->eventID());
176
177        (*it).event->setEventID(0);
178        it = mQueue.erase(it);
179
180        if (stopAfterFirstMatch) {
181            return;
182        }
183    }
184}
185
186// static
187int64_t TimedEventQueue::getRealTimeUs() {
188    struct timeval tv;
189    gettimeofday(&tv, NULL);
190
191    return (int64_t)tv.tv_sec * 1000000ll + tv.tv_usec;
192}
193
194// static
195void *TimedEventQueue::ThreadWrapper(void *me) {
196
197#ifdef ANDROID_SIMULATOR
198    // The simulator runs everything as one process, so any
199    // Binder calls happen on this thread instead of a thread
200    // in another process. We therefore need to make sure that
201    // this thread can do calls into interpreted code.
202    // On the device this is not an issue because the remote
203    // thread will already be set up correctly for this.
204    JavaVM *vm;
205    int numvms;
206    JNI_GetCreatedJavaVMs(&vm, 1, &numvms);
207    JNIEnv *env;
208    vm->AttachCurrentThread(&env, NULL);
209#endif
210
211    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_FOREGROUND);
212    static_cast<TimedEventQueue *>(me)->threadEntry();
213
214#ifdef ANDROID_SIMULATOR
215    vm->DetachCurrentThread();
216#endif
217    return NULL;
218}
219
220void TimedEventQueue::threadEntry() {
221    prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);
222
223    for (;;) {
224        int64_t now_us = 0;
225        sp<Event> event;
226
227        {
228            Mutex::Autolock autoLock(mLock);
229
230            if (mStopped) {
231                break;
232            }
233
234            while (mQueue.empty()) {
235                mQueueNotEmptyCondition.wait(mLock);
236            }
237
238            event_id eventID = 0;
239            for (;;) {
240                if (mQueue.empty()) {
241                    // The only event in the queue could have been cancelled
242                    // while we were waiting for its scheduled time.
243                    break;
244                }
245
246                List<QueueItem>::iterator it = mQueue.begin();
247                eventID = (*it).event->eventID();
248
249                now_us = getRealTimeUs();
250                int64_t when_us = (*it).realtime_us;
251
252                int64_t delay_us;
253                if (when_us < 0 || when_us == INT64_MAX) {
254                    delay_us = 0;
255                } else {
256                    delay_us = when_us - now_us;
257                }
258
259                if (delay_us <= 0) {
260                    break;
261                }
262
263                static int64_t kMaxTimeoutUs = 10000000ll;  // 10 secs
264                bool timeoutCapped = false;
265                if (delay_us > kMaxTimeoutUs) {
266                    LOGW("delay_us exceeds max timeout: %lld us", delay_us);
267
268                    // We'll never block for more than 10 secs, instead
269                    // we will split up the full timeout into chunks of
270                    // 10 secs at a time. This will also avoid overflow
271                    // when converting from us to ns.
272                    delay_us = kMaxTimeoutUs;
273                    timeoutCapped = true;
274                }
275
276                status_t err = mQueueHeadChangedCondition.waitRelative(
277                        mLock, delay_us * 1000ll);
278
279                if (!timeoutCapped && err == -ETIMEDOUT) {
280                    // We finally hit the time this event is supposed to
281                    // trigger.
282                    now_us = getRealTimeUs();
283                    break;
284                }
285            }
286
287            // The event w/ this id may have been cancelled while we're
288            // waiting for its trigger-time, in that case
289            // removeEventFromQueue_l will return NULL.
290            // Otherwise, the QueueItem will be removed
291            // from the queue and the referenced event returned.
292            event = removeEventFromQueue_l(eventID);
293        }
294
295        if (event != NULL) {
296            // Fire event with the lock NOT held.
297            event->fire(this, now_us);
298        }
299    }
300}
301
302sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
303        event_id id) {
304    for (List<QueueItem>::iterator it = mQueue.begin();
305         it != mQueue.end(); ++it) {
306        if ((*it).event->eventID() == id) {
307            sp<Event> event = (*it).event;
308            event->setEventID(0);
309
310            mQueue.erase(it);
311
312            return event;
313        }
314    }
315
316    LOGW("Event %d was not found in the queue, already cancelled?", id);
317
318    return NULL;
319}
320
321}  // namespace android
322
323