blob: 6d345bb7a02ad7adf18eea414810d8bb539ad99d [file] [log] [blame]
Andreas Hubere46b7be2009-07-14 16:56:47 -07001/*
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
Marco Nelissen7013c8b2009-07-16 17:10:51 -070019#define __STDC_LIMIT_MACROS
Andreas Hubere46b7be2009-07-14 16:56:47 -070020#include <stdint.h>
21
Andreas Huberea1d6712010-04-12 13:10:20 -070022//#define LOG_NDEBUG 0
Andreas Hubere46b7be2009-07-14 16:56:47 -070023#define LOG_TAG "TimedEventQueue"
24#include <utils/Log.h>
Marco Nelissencad57262010-06-15 12:34:50 -070025#include <utils/threads.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070026
Andreas Huberbd7b43b2009-10-13 10:22:55 -070027#include "include/TimedEventQueue.h"
28
Andreas Huber62141b42010-03-29 11:52:08 -070029#include <sys/prctl.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070030#include <sys/time.h>
31
James Dong8e9d67a2012-02-06 23:46:37 -080032#include <media/stagefright/foundation/ADebug.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070033
Marco Nelissen7691af92010-02-19 15:16:57 -080034#ifdef ANDROID_SIMULATOR
35#include <jni.h>
36#endif
37
Andreas Hubere46b7be2009-07-14 16:56:47 -070038namespace android {
39
40TimedEventQueue::TimedEventQueue()
Andreas Huber9c540102009-11-17 10:25:01 -080041 : mNextEventID(1),
42 mRunning(false),
Andreas Hubere46b7be2009-07-14 16:56:47 -070043 mStopped(false) {
44}
45
46TimedEventQueue::~TimedEventQueue() {
47 stop();
48}
49
50void TimedEventQueue::start() {
51 if (mRunning) {
52 return;
53 }
54
55 mStopped = false;
56
57 pthread_attr_t attr;
58 pthread_attr_init(&attr);
59 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
60
61 pthread_create(&mThread, &attr, ThreadWrapper, this);
62
63 pthread_attr_destroy(&attr);
64
65 mRunning = true;
66}
67
68void TimedEventQueue::stop(bool flush) {
69 if (!mRunning) {
70 return;
71 }
72
73 if (flush) {
74 postEventToBack(new StopEvent);
75 } else {
76 postTimedEvent(new StopEvent, INT64_MIN);
77 }
78
79 void *dummy;
80 pthread_join(mThread, &dummy);
81
82 mQueue.clear();
83
84 mRunning = false;
85}
86
Andreas Huber9c540102009-11-17 10:25:01 -080087TimedEventQueue::event_id TimedEventQueue::postEvent(const sp<Event> &event) {
Andreas Hubere46b7be2009-07-14 16:56:47 -070088 // Reserve an earlier timeslot an INT64_MIN to be able to post
89 // the StopEvent to the absolute head of the queue.
Andreas Huber9c540102009-11-17 10:25:01 -080090 return postTimedEvent(event, INT64_MIN + 1);
Andreas Hubere46b7be2009-07-14 16:56:47 -070091}
92
Andreas Huber9c540102009-11-17 10:25:01 -080093TimedEventQueue::event_id TimedEventQueue::postEventToBack(
94 const sp<Event> &event) {
95 return postTimedEvent(event, INT64_MAX);
Andreas Hubere46b7be2009-07-14 16:56:47 -070096}
97
Andreas Huber9c540102009-11-17 10:25:01 -080098TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(
Andreas Hubere46b7be2009-07-14 16:56:47 -070099 const sp<Event> &event, int64_t delay_us) {
Andreas Huberb5ceb9e2009-08-26 14:48:20 -0700100 CHECK(delay_us >= 0);
Andreas Huber9c540102009-11-17 10:25:01 -0800101 return postTimedEvent(event, getRealTimeUs() + delay_us);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700102}
103
Andreas Huber9c540102009-11-17 10:25:01 -0800104TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
Andreas Hubere46b7be2009-07-14 16:56:47 -0700105 const sp<Event> &event, int64_t realtime_us) {
106 Mutex::Autolock autoLock(mLock);
107
Andreas Huber9c540102009-11-17 10:25:01 -0800108 event->setEventID(mNextEventID++);
109
Andreas Hubere46b7be2009-07-14 16:56:47 -0700110 List<QueueItem>::iterator it = mQueue.begin();
111 while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
112 ++it;
113 }
114
115 QueueItem item;
116 item.event = event;
117 item.realtime_us = realtime_us;
118
119 if (it == mQueue.begin()) {
120 mQueueHeadChangedCondition.signal();
121 }
122
123 mQueue.insert(it, item);
124
125 mQueueNotEmptyCondition.signal();
Andreas Huber9c540102009-11-17 10:25:01 -0800126
127 return event->eventID();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700128}
129
Andreas Huber9c540102009-11-17 10:25:01 -0800130static bool MatchesEventID(
131 void *cookie, const sp<TimedEventQueue::Event> &event) {
132 TimedEventQueue::event_id *id =
133 static_cast<TimedEventQueue::event_id *>(cookie);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700134
Andreas Huber9c540102009-11-17 10:25:01 -0800135 if (event->eventID() != *id) {
Andreas Hubere46b7be2009-07-14 16:56:47 -0700136 return false;
137 }
138
Andreas Huber9c540102009-11-17 10:25:01 -0800139 *id = 0;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700140
141 return true;
142}
143
Andreas Huber9c540102009-11-17 10:25:01 -0800144bool TimedEventQueue::cancelEvent(event_id id) {
Andreas Huber27366fc2009-11-20 09:32:46 -0800145 if (id == 0) {
146 return false;
147 }
Andreas Huber9c540102009-11-17 10:25:01 -0800148
149 cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
150
151 // if MatchesEventID found a match, it will have set id to 0
152 // (which is not a valid event_id).
153
154 return id == 0;
155}
156
157void TimedEventQueue::cancelEvents(
158 bool (*predicate)(void *cookie, const sp<Event> &event),
159 void *cookie,
160 bool stopAfterFirstMatch) {
161 Mutex::Autolock autoLock(mLock);
162
163 List<QueueItem>::iterator it = mQueue.begin();
164 while (it != mQueue.end()) {
165 if (!(*predicate)(cookie, (*it).event)) {
166 ++it;
167 continue;
168 }
169
170 if (it == mQueue.begin()) {
171 mQueueHeadChangedCondition.signal();
172 }
173
Steve Block71f2cf12011-10-20 11:56:00 +0100174 ALOGV("cancelling event %d", (*it).event->eventID());
Andreas Huberea1d6712010-04-12 13:10:20 -0700175
Andreas Huber27366fc2009-11-20 09:32:46 -0800176 (*it).event->setEventID(0);
Andreas Huber9c540102009-11-17 10:25:01 -0800177 it = mQueue.erase(it);
178
179 if (stopAfterFirstMatch) {
180 return;
181 }
182 }
183}
184
Andreas Hubere46b7be2009-07-14 16:56:47 -0700185// static
186int64_t TimedEventQueue::getRealTimeUs() {
187 struct timeval tv;
188 gettimeofday(&tv, NULL);
189
Andreas Huber62141b42010-03-29 11:52:08 -0700190 return (int64_t)tv.tv_sec * 1000000ll + tv.tv_usec;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700191}
192
193// static
194void *TimedEventQueue::ThreadWrapper(void *me) {
Marco Nelissen7691af92010-02-19 15:16:57 -0800195
196#ifdef ANDROID_SIMULATOR
197 // The simulator runs everything as one process, so any
198 // Binder calls happen on this thread instead of a thread
199 // in another process. We therefore need to make sure that
200 // this thread can do calls into interpreted code.
201 // On the device this is not an issue because the remote
202 // thread will already be set up correctly for this.
203 JavaVM *vm;
204 int numvms;
205 JNI_GetCreatedJavaVMs(&vm, 1, &numvms);
206 JNIEnv *env;
207 vm->AttachCurrentThread(&env, NULL);
208#endif
209
Glenn Kasten1d24aaa2011-06-14 10:35:34 -0700210 androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
Andreas Huber4b3d32b2010-09-09 16:12:31 -0700211
Andreas Hubere46b7be2009-07-14 16:56:47 -0700212 static_cast<TimedEventQueue *>(me)->threadEntry();
213
Marco Nelissen7691af92010-02-19 15:16:57 -0800214#ifdef ANDROID_SIMULATOR
215 vm->DetachCurrentThread();
216#endif
Andreas Hubere46b7be2009-07-14 16:56:47 -0700217 return NULL;
218}
219
220void TimedEventQueue::threadEntry() {
Andreas Huber62141b42010-03-29 11:52:08 -0700221 prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);
222
Andreas Hubere46b7be2009-07-14 16:56:47 -0700223 for (;;) {
Andreas Huber62141b42010-03-29 11:52:08 -0700224 int64_t now_us = 0;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700225 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
Andreas Huberea1d6712010-04-12 13:10:20 -0700238 event_id eventID = 0;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700239 for (;;) {
Andreas Huber62141b42010-03-29 11:52:08 -0700240 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 }
Andreas Huberea1d6712010-04-12 13:10:20 -0700245
246 List<QueueItem>::iterator it = mQueue.begin();
247 eventID = (*it).event->eventID();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700248
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
Andreas Huber62141b42010-03-29 11:52:08 -0700263 static int64_t kMaxTimeoutUs = 10000000ll; // 10 secs
264 bool timeoutCapped = false;
265 if (delay_us > kMaxTimeoutUs) {
Steve Block8564c8d2012-01-05 23:22:43 +0000266 ALOGW("delay_us exceeds max timeout: %lld us", delay_us);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700267
Andreas Huber62141b42010-03-29 11:52:08 -0700268 // 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.
Andreas Hubere46b7be2009-07-14 16:56:47 -0700282 now_us = getRealTimeUs();
283 break;
284 }
285 }
286
Andreas Huberea1d6712010-04-12 13:10:20 -0700287 // 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);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700293 }
294
Andreas Huberea1d6712010-04-12 13:10:20 -0700295 if (event != NULL) {
296 // Fire event with the lock NOT held.
297 event->fire(this, now_us);
298 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700299 }
300}
301
Andreas Huberea1d6712010-04-12 13:10:20 -0700302sp<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
Steve Block8564c8d2012-01-05 23:22:43 +0000316 ALOGW("Event %d was not found in the queue, already cancelled?", id);
Andreas Huberea1d6712010-04-12 13:10:20 -0700317
318 return NULL;
319}
320
Andreas Hubere46b7be2009-07-14 16:56:47 -0700321} // namespace android
322