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