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