diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/audioflinger/Android.mk | 2 | ||||
-rw-r--r-- | services/audioflinger/StateQueue.cpp | 133 | ||||
-rw-r--r-- | services/audioflinger/StateQueue.h | 94 |
3 files changed, 229 insertions, 0 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 2cf77ce..2f78a7e 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -34,6 +34,8 @@ LOCAL_SRC_FILES:= \ # AudioResamplerSinc.cpp.arm # AudioResamplerCubic.cpp.arm +LOCAL_SRC_FILES += StateQueue.cpp + LOCAL_C_INCLUDES := \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) 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 diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h new file mode 100644 index 0000000..fe72ddc --- /dev/null +++ b/services/audioflinger/StateQueue.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef ANDROID_AUDIO_STATE_QUEUE_H +#define ANDROID_AUDIO_STATE_QUEUE_H + +namespace android { + +// manages a FIFO queue of states +template<typename T> class StateQueue { + +public: + StateQueue(); + virtual ~StateQueue(); + + // Observer APIs + + // Poll for a state change. Returns a pointer to a read-only state, + // or NULL if the state has not been initialized yet. + // If a new state has not pushed by mutator since the previous poll, + // then the returned pointer will be unchanged. + // The previous state pointer is guaranteed to still be valid; + // this allows the observer to diff the previous and new states. + const T* poll(); + + // Mutator APIs + + // Begin a mutation. Returns a pointer to a read/write state, except the + // first time it is called the state is write-only and _must_ be initialized. + // Mutations cannot be nested. + // If the state is dirty and has not been pushed onto the state queue yet, then + // this new mutation will be squashed together with the previous one. + T* begin(); + + // End the current mutation and indicate whether caller modified the state. + // If didModify is true, then the state is marked dirty (in need of pushing). + // There is no rollback option because modifications are done in place. + // Does not automatically push the new state onto the state queue. + void end(bool didModify = true); + + // Push a new state, if any, out to the observer via the state queue. + // For BLOCK_NEVER, returns: + // true if not dirty, or dirty and pushed successfully + // false if dirty and not pushed because that would block; remains dirty + // For BLOCK_UNTIL_PUSHED and BLOCK_UNTIL_ACKED, always returns true. + // No-op if there are no pending modifications (not dirty), except + // for BLOCK_UNTIL_ACKED it will wait until a prior push has been acknowledged. + // Must not be called in the middle of a mutation. + enum block_t { + BLOCK_NEVER, // do not block + BLOCK_UNTIL_PUSHED, // block until there's a slot available for the push + BLOCK_UNTIL_ACKED, // also block until the push is acknowledged by the observer + }; + bool push(block_t block = BLOCK_NEVER); + + // Return whether the current state is dirty (modified and not pushed). + bool isDirty() const { return mIsDirty; } + +private: + static const unsigned kN = 4; // values != 4 are not supported by this code + T mStates[kN]; // written by mutator, read by observer + + // "volatile" is meaningless with SMP, but here it indicates that we're using atomic ops + volatile const T* mNext; // written by mutator to advance next, read by observer + volatile const T* mAck; // written by observer to acknowledge advance of next, read by mutator + + // only used by observer + const T* mCurrent; // most recent value returned by poll() + + // only used by mutator + T* mMutating; // where updates by mutator are done in place + const T* mExpecting; // what the mutator expects mAck to be set to + bool mInMutation; // whether we're currently in the middle of a mutation + bool mIsDirty; // whether mutating state has been modified since last push + bool mIsInitialized; // whether mutating state has been initialized yet + +}; // class StateQueue + +} // namespace android + +#endif // ANDROID_AUDIO_STATE_QUEUE_H |