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