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