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