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