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