StateQueue.cpp revision dc998c809e084b617990b281e2ed5271830cc2e0
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// Constructor and destructor 28 29template<typename T> StateQueue<T>::StateQueue() : 30 mNext(NULL), mAck(NULL), mCurrent(NULL), 31 mMutating(&mStates[0]), mExpecting(NULL), 32 mInMutation(false), mIsDirty(false), mIsInitialized(false) 33{ 34} 35 36template<typename T> StateQueue<T>::~StateQueue() 37{ 38} 39 40// Observer APIs 41 42template<typename T> const T* StateQueue<T>::poll() 43{ 44 const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext); 45 if (next != mCurrent) { 46 mAck = next; // no additional barrier needed 47 mCurrent = next; 48 } 49 return next; 50} 51 52// Mutator APIs 53 54template<typename T> T* StateQueue<T>::begin() 55{ 56 ALOG_ASSERT(!mInMutation, "begin() called when in a mutation"); 57 mInMutation = true; 58 return mMutating; 59} 60 61template<typename T> void StateQueue<T>::end(bool didModify) 62{ 63 ALOG_ASSERT(mInMutation, "end() called when not in a mutation"); 64 ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization"); 65 if (didModify) { 66 mIsDirty = true; 67 mIsInitialized = true; 68 } 69 mInMutation = false; 70} 71 72template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block) 73{ 74#define PUSH_BLOCK_ACK_NS 3000000L // 3 ms: time between checks for ack in push() 75 // FIXME should be configurable 76 static const struct timespec req = {0, PUSH_BLOCK_ACK_NS}; 77 78 ALOG_ASSERT(!mInMutation, "push() called when in a mutation"); 79 80 if (mIsDirty) { 81 82 // wait for prior push to be acknowledged 83 if (mExpecting != NULL) { 84 for (;;) { 85 const T *ack = (const T *) mAck; // no additional barrier needed 86 if (ack == mExpecting) { 87 // unnecessary as we're about to rewrite 88 //mExpecting = NULL; 89 break; 90 } 91 if (block == BLOCK_NEVER) { 92 return false; 93 } 94 nanosleep(&req, NULL); 95 } 96 } 97 98 // publish 99 android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext); 100 mExpecting = mMutating; 101 102 // copy with circular wraparound 103 if (++mMutating >= &mStates[kN]) { 104 mMutating = &mStates[0]; 105 } 106 *mMutating = *mExpecting; 107 mIsDirty = false; 108 109 } 110 111 // optionally wait for this push or a prior push to be acknowledged 112 if (block == BLOCK_UNTIL_ACKED) { 113 if (mExpecting != NULL) { 114 for (;;) { 115 const T *ack = (const T *) mAck; // no additional barrier needed 116 if (ack == mExpecting) { 117 mExpecting = NULL; 118 break; 119 } 120 nanosleep(&req, NULL); 121 } 122 } 123 } 124 125 return true; 126} 127 128} // namespace android 129 130// hack for gcc 131#ifdef STATE_QUEUE_INSTANTIATIONS 132#include STATE_QUEUE_INSTANTIATIONS 133#endif 134