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 <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