From 2188bc912a56d9bc577fcec7bf2208f49455e744 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 26 Oct 2012 16:10:30 -0700 Subject: StateQueue comments Change-Id: Id8c76d1e2ab5201e7ab3875f1dbcc0a126e611a4 --- services/audioflinger/StateQueue.h | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'services/audioflinger/StateQueue.h') diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h index eba190c..c9b5111 100644 --- a/services/audioflinger/StateQueue.h +++ b/services/audioflinger/StateQueue.h @@ -17,6 +17,72 @@ #ifndef ANDROID_AUDIO_STATE_QUEUE_H #define ANDROID_AUDIO_STATE_QUEUE_H +// The state queue template class was originally driven by this use case / requirements: +// There are two threads: a fast mixer, and a normal mixer, and they share state. +// The interesting part of the shared state is a set of active fast tracks, +// and the output HAL configuration (buffer size in frames, sample rate, etc.). +// Fast mixer thread: +// periodic with typical period < 10 ms +// FIFO/RR scheduling policy and a low fixed priority +// ok to block for bounded time using nanosleep() to achieve desired period +// must not block on condition wait, mutex lock, atomic operation spin, I/O, etc. +// under typical operations of mixing, writing, or adding/removing tracks +// ok to block for unbounded time when the output HAL configuration changes, +// and this may result in an audible artifact +// needs read-only access to a recent stable state, +// but not necessarily the most current one +// Normal mixer thread: +// periodic with typical period ~40 ms +// SCHED_OTHER scheduling policy and nice priority == urgent audio +// ok to block, but prefer to avoid as much as possible +// needs read/write access to state +// The normal mixer may need to temporarily suspend the fast mixer thread during mode changes. +// It will do this using the state -- one of the fields tells the fast mixer to idle. + +// Additional requirements: +// - observer must always be able to poll for and view the latest pushed state; it must never be +// blocked from seeing that state +// - observer does not need to see every state in sequence; it is OK for it to skip states +// [see below for more on this] +// - mutator must always be able to read/modify a state, it must never be blocked from reading or +// modifying state +// - reduce memcpy where possible +// - work well if the observer runs more frequently than the mutator, +// as is the case with fast mixer/normal mixer. +// It is not a requirement to work well if the roles were reversed, +// and the mutator were to run more frequently than the observer. +// In this case, the mutator could get blocked waiting for a slot to fill up for +// it to work with. This could be solved somewhat by increasing the depth of the queue, but it would +// still limit the mutator to a finite number of changes before it would block. A future +// possibility, not implemented here, would be to allow the mutator to safely overwrite an already +// pushed state. This could be done by the mutator overwriting mNext, but then being prepared to +// read an mAck which is actually for the earlier mNext (since there is a race). + +// Solution: +// Let's call the fast mixer thread the "observer" and normal mixer thread the "mutator". +// We assume there is only a single observer and a single mutator; this is critical. +// Each state is of type , and should contain only POD (Plain Old Data) and raw pointers, as +// memcpy() may be used to copy state, and the destructors are run in unpredictable order. +// The states in chronological order are: previous, current, next, and mutating: +// previous read-only, observer can compare vs. current to see the subset that changed +// current read-only, this is the primary state for observer +// next read-only, when observer is ready to accept a new state it will shift it in: +// previous = current +// current = next +// and the slot formerly used by previous is now available to the mutator. +// mutating invisible to observer, read/write to mutator +// Initialization is tricky, especially for the observer. If the observer starts execution +// before the mutator, there are no previous, current, or next states. And even if the observer +// starts execution after the mutator, there is a next state but no previous or current states. +// To solve this, we'll have the observer idle until there is a next state, +// and it will have to deal with the case where there is no previous state. +// The states are stored in a shared FIFO queue represented using a circular array. +// The observer polls for mutations, and receives a new state pointer after a +// a mutation is pushed onto the queue. To the observer, the state pointers are +// effectively in random order, that is the observer should not do address +// arithmetic on the state pointers. However to the mutator, the state pointers +// are in a definite circular order. + namespace android { #ifdef STATE_QUEUE_DUMP -- cgit v1.1