StateQueue.cpp revision 399930859a75d806ce0ef124ac22025ae4ef0549
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 25namespace android { 26 27#ifdef STATE_QUEUE_DUMP 28void StateQueueObserverDump::dump(int fd) 29{ 30 fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); 31} 32 33void 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 42template<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 52template<typename T> StateQueue<T>::~StateQueue() 53{ 54} 55 56// Observer APIs 57 58template<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 73template<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 80template<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 91template<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