Home | History | Annotate | Download | only in libstagefright
      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