Home | History | Annotate | Download | only in audioflinger
      1 /*
      2  * Copyright (C) 2012 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 #define LOG_TAG "StateQueue"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <time.h>
     21 #include <cutils/atomic.h>
     22 #include <utils/Log.h>
     23 #include "StateQueue.h"
     24 
     25 namespace android {
     26 
     27 #ifdef STATE_QUEUE_DUMP
     28 void StateQueueObserverDump::dump(int fd)
     29 {
     30     fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
     31 }
     32 
     33 void StateQueueMutatorDump::dump(int fd)
     34 {
     35     fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
     36             mPushDirty, mPushAck, mBlockedSequence);
     37 }
     38 #endif
     39 
     40 // Constructor and destructor
     41 
     42 template<typename T> StateQueue<T>::StateQueue() :
     43     mNext(NULL), mAck(NULL), mCurrent(NULL),
     44     mMutating(&mStates[0]), mExpecting(NULL),
     45     mInMutation(false), mIsDirty(false), mIsInitialized(false)
     46 #ifdef STATE_QUEUE_DUMP
     47     , mObserverDump(&mObserverDummyDump), mMutatorDump(&mMutatorDummyDump)
     48 #endif
     49 {
     50 }
     51 
     52 template<typename T> StateQueue<T>::~StateQueue()
     53 {
     54 }
     55 
     56 // Observer APIs
     57 
     58 template<typename T> const T* StateQueue<T>::poll()
     59 {
     60     const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext);
     61     if (next != mCurrent) {
     62         mAck = next;    // no additional barrier needed
     63         mCurrent = next;
     64 #ifdef STATE_QUEUE_DUMP
     65         mObserverDump->mStateChanges++;
     66 #endif
     67     }
     68     return next;
     69 }
     70 
     71 // Mutator APIs
     72 
     73 template<typename T> T* StateQueue<T>::begin()
     74 {
     75     ALOG_ASSERT(!mInMutation, "begin() called when in a mutation");
     76     mInMutation = true;
     77     return mMutating;
     78 }
     79 
     80 template<typename T> void StateQueue<T>::end(bool didModify)
     81 {
     82     ALOG_ASSERT(mInMutation, "end() called when not in a mutation");
     83     ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization");
     84     if (didModify) {
     85         mIsDirty = true;
     86         mIsInitialized = true;
     87     }
     88     mInMutation = false;
     89 }
     90 
     91 template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
     92 {
     93 #define PUSH_BLOCK_ACK_NS    3000000L   // 3 ms: time between checks for ack in push()
     94                                         //       FIXME should be configurable
     95     static const struct timespec req = {0, PUSH_BLOCK_ACK_NS};
     96 
     97     ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
     98 
     99 #ifdef STATE_QUEUE_DUMP
    100     if (block == BLOCK_UNTIL_ACKED) {
    101         mMutatorDump->mPushAck++;
    102     }
    103 #endif
    104 
    105     if (mIsDirty) {
    106 
    107 #ifdef STATE_QUEUE_DUMP
    108         mMutatorDump->mPushDirty++;
    109 #endif
    110 
    111         // wait for prior push to be acknowledged
    112         if (mExpecting != NULL) {
    113 #ifdef STATE_QUEUE_DUMP
    114             unsigned count = 0;
    115 #endif
    116             for (;;) {
    117                 const T *ack = (const T *) mAck;    // no additional barrier needed
    118                 if (ack == mExpecting) {
    119                     // unnecessary as we're about to rewrite
    120                     //mExpecting = NULL;
    121                     break;
    122                 }
    123                 if (block == BLOCK_NEVER) {
    124                     return false;
    125                 }
    126 #ifdef STATE_QUEUE_DUMP
    127                 if (count == 1) {
    128                     mMutatorDump->mBlockedSequence++;
    129                 }
    130                 ++count;
    131 #endif
    132                 nanosleep(&req, NULL);
    133             }
    134 #ifdef STATE_QUEUE_DUMP
    135             if (count > 1) {
    136                 mMutatorDump->mBlockedSequence++;
    137             }
    138 #endif
    139         }
    140 
    141         // publish
    142         android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext);
    143         mExpecting = mMutating;
    144 
    145         // copy with circular wraparound
    146         if (++mMutating >= &mStates[kN]) {
    147             mMutating = &mStates[0];
    148         }
    149         *mMutating = *mExpecting;
    150         mIsDirty = false;
    151 
    152     }
    153 
    154     // optionally wait for this push or a prior push to be acknowledged
    155     if (block == BLOCK_UNTIL_ACKED) {
    156         if (mExpecting != NULL) {
    157 #ifdef STATE_QUEUE_DUMP
    158             unsigned count = 0;
    159 #endif
    160             for (;;) {
    161                 const T *ack = (const T *) mAck;    // no additional barrier needed
    162                 if (ack == mExpecting) {
    163                     mExpecting = NULL;
    164                     break;
    165                 }
    166 #ifdef STATE_QUEUE_DUMP
    167                 if (count == 1) {
    168                     mMutatorDump->mBlockedSequence++;
    169                 }
    170                 ++count;
    171 #endif
    172                 nanosleep(&req, NULL);
    173             }
    174 #ifdef STATE_QUEUE_DUMP
    175             if (count > 1) {
    176                 mMutatorDump->mBlockedSequence++;
    177             }
    178 #endif
    179         }
    180     }
    181 
    182     return true;
    183 }
    184 
    185 }   // namespace android
    186 
    187 // hack for gcc
    188 #ifdef STATE_QUEUE_INSTANTIATIONS
    189 #include STATE_QUEUE_INSTANTIATIONS
    190 #endif
    191