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