diff options
Diffstat (limited to 'services/audioflinger/StateQueue.cpp')
-rw-r--r-- | services/audioflinger/StateQueue.cpp | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp new file mode 100644 index 0000000..ae263f5 --- /dev/null +++ b/services/audioflinger/StateQueue.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "StateQueue" +//#define LOG_NDEBUG 0 + +#include <time.h> +#include <cutils/atomic.h> +#include <utils/Log.h> +#include "StateQueue.h" + +namespace android { + +// Constructor and destructor + +template<typename T> StateQueue<T>::StateQueue() : + mNext(NULL), mAck(NULL), mCurrent(NULL), + mMutating(&mStates[0]), mExpecting(NULL), + mInMutation(false), mIsDirty(false), mIsInitialized(false) +{ +} + +template<typename T> StateQueue<T>::~StateQueue() +{ +} + +// Observer APIs + +template<typename T> const T* StateQueue<T>::poll() +{ + const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext); + if (next != mCurrent) { + mAck = next; // no additional barrier needed + mCurrent = next; + } + return next; +} + +// Mutator APIs + +template<typename T> T* StateQueue<T>::begin() +{ + ALOG_ASSERT(!mInMutation, "begin() called when in a mutation"); + mInMutation = true; + return mMutating; +} + +template<typename T> void StateQueue<T>::end(bool didModify) +{ + ALOG_ASSERT(mInMutation, "end() called when not in a mutation"); + ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization"); + if (didModify) { + mIsDirty = true; + mIsInitialized = true; + } + mInMutation = false; +} + +template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block) +{ +#define PUSH_BLOCK_ACK_NS 3000000L // 3 ms: time between checks for ack in push() + // FIXME should be configurable + static const struct timespec req = {0, PUSH_BLOCK_ACK_NS}; + + ALOG_ASSERT(!mInMutation, "push() called when in a mutation"); + + if (mIsDirty) { + + // wait for prior push to be acknowledged + if (mExpecting != NULL) { + for (;;) { + const T *ack = (const T *) mAck; // no additional barrier needed + if (ack == mExpecting) { + // unnecessary as we're about to rewrite + //mExpecting = NULL; + break; + } + if (block == BLOCK_NEVER) { + return false; + } + nanosleep(&req, NULL); + } + } + + // publish + android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext); + mExpecting = mMutating; + + // copy with circular wraparound + if (++mMutating >= &mStates[kN]) { + mMutating = &mStates[0]; + } + *mMutating = *mExpecting; + mIsDirty = false; + + } + + // optionally wait for this push or a prior push to be acknowledged + if (block == BLOCK_UNTIL_ACKED) { + if (mExpecting != NULL) { + for (;;) { + const T *ack = (const T *) mAck; // no additional barrier needed + if (ack == mExpecting) { + mExpecting = NULL; + break; + } + nanosleep(&req, NULL); + } + } + } + + return true; +} + +} // namespace android + +// hack for gcc +#ifdef STATE_QUEUE_INSTANTIATIONS +#include STATE_QUEUE_INSTANTIATIONS +#endif |