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