fifo: add index references with caching and deferred operations

The new classes will eventually permit a set of related atomic operations,
wakes, and waits to be grouped together into a single sequence point,
and will allow use of event flags to wait for the "or" of multiple FIFOs.

This is one of a series of CLs to isolate the dependencies.

Test: not ready yet for testing
Change-Id: I2055b7a35721222cd914973cd210f5c0dca7d4ef
diff --git a/audio_utils/fifo_index.cpp b/audio_utils/fifo_index.cpp
index b35319c..ab8292f 100644
--- a/audio_utils/fifo_index.cpp
+++ b/audio_utils/fifo_index.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <limits.h>
 #include <audio_utils/fifo_index.h>
 #include <audio_utils/futex.h>
 
@@ -39,3 +41,117 @@
 {
     return sys_futex(&mIndex, op, waiters, NULL, NULL, 0);
 }
+
+////
+
+RefIndexDeferredStoreReleaseDeferredWake::RefIndexDeferredStoreReleaseDeferredWake(
+        audio_utils_fifo_index& index)
+    : mIndex(index), mValue(0), mWriteback(false), mWaiters(0), mWakeOp(FUTEX_WAIT_PRIVATE)
+{
+}
+
+RefIndexDeferredStoreReleaseDeferredWake::~RefIndexDeferredStoreReleaseDeferredWake()
+{
+    writeback();
+    wakeNowIfNeeded();
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::set(uint32_t value) {
+    mValue = value;
+    mWriteback = true;
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::writeback()
+{
+    if (mWriteback) {
+        // TODO When part of a collection, should use relaxed for all but the last writeback
+        mIndex.storeRelease(mValue);
+        mWriteback = false;
+    }
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::writethrough(uint32_t value) {
+    set(value);
+    writeback();
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::wakeDeferred(int op, int waiters)
+{
+    if (waiters <= 0) {
+        return;
+    }
+    // default is FUTEX_WAKE_PRIVATE
+    if (op == FUTEX_WAKE) {
+        mWakeOp = FUTEX_WAKE;
+    }
+    if (waiters < INT_MAX - mWaiters) {
+        mWaiters += waiters;
+    } else {
+        mWaiters = INT_MAX;
+    }
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::wakeNowIfNeeded()
+{
+    if (mWaiters > 0) {
+        mIndex.wake(mWakeOp, mWaiters);
+        mWaiters = 0;
+        mWakeOp = FUTEX_WAKE_PRIVATE;
+    }
+}
+
+void RefIndexDeferredStoreReleaseDeferredWake::wakeNow(int op, int waiters)
+{
+    wakeDeferred(op, waiters);
+    wakeNowIfNeeded();
+}
+
+////
+
+RefIndexCachedLoadAcquireDeferredWait::RefIndexCachedLoadAcquireDeferredWait(
+        audio_utils_fifo_index& index)
+    : mIndex(index), mValue(0), mLoaded(false)
+{
+}
+
+RefIndexCachedLoadAcquireDeferredWait::~RefIndexCachedLoadAcquireDeferredWait()
+{
+}
+
+uint32_t RefIndexCachedLoadAcquireDeferredWait::get()
+{
+    prefetch();
+    return mValue;
+}
+
+void RefIndexCachedLoadAcquireDeferredWait::prefetch()
+{
+    if (!mLoaded) {
+        // TODO When part of a collection, should use relaxed for all but the last load
+        mValue = mIndex.loadAcquire();
+        mLoaded = true;
+    }
+}
+
+void RefIndexCachedLoadAcquireDeferredWait::invalidate()
+{
+    mLoaded = false;
+}
+
+#if 0
+uint32_t RefIndexCachedLoadAcquireDeferredWait::readthrough()
+{
+    invalidate();
+    return get();
+}
+#endif
+
+int RefIndexCachedLoadAcquireDeferredWait::wait(int op, const struct timespec *timeout)
+{
+    if (!mLoaded) {
+        return -EINVAL;
+    }
+    int err = mIndex.wait(op, mValue /*expected*/, timeout);
+    invalidate();
+    return err;
+}