Pipe throttle based on requested frame count

Now does throttling based on the requested frame count, but
still permits overfilling pipe up to the rounded-up frame count.
Also still uses the larger frame count for non-blocking arithmetic.

Bug: 6585323
Change-Id: Ic2420a071419905524a14c22ebe1ce0e32d70a9d
diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp
index fd16e92..6efb8b1 100644
--- a/services/audioflinger/MonoPipe.cpp
+++ b/services/audioflinger/MonoPipe.cpp
@@ -25,9 +25,10 @@
 
 namespace android {
 
-MonoPipe::MonoPipe(size_t maxFrames, NBAIO_Format format, bool writeCanBlock) :
+MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
         NBAIO_Sink(format),
-        mMaxFrames(roundup(maxFrames)),
+        mReqFrames(reqFrames),
+        mMaxFrames(roundup(reqFrames)),
         mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
         mFront(0),
         mRear(0),
@@ -45,6 +46,7 @@
     if (CC_UNLIKELY(!mNegotiated)) {
         return NEGOTIATE;
     }
+    // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit
     ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
     ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
     return ret;
@@ -57,6 +59,7 @@
     }
     size_t totalFramesWritten = 0;
     while (count > 0) {
+        // can't return a negative value, as we already checked for !mNegotiated
         size_t avail = availableToWrite();
         size_t written = avail;
         if (CC_LIKELY(written > count)) {
@@ -84,50 +87,29 @@
         count -= written;
         buffer = (char *) buffer + (written << mBitShift);
         // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
-        // The throttle tries to keep the pipe about 5/8 full on average, with a slight jitter.
-        uint64_t ns;
-        enum {
-            THROTTLE_VERY_FAST, // pipe is (nearly) empty, fill quickly
-            THROTTLE_FAST,      // pipe is normal, fill at slightly faster rate
-            THROTTLE_NOMINAL,   // pipe is normal, fill at nominal rate
-            THROTTLE_SLOW,      // pipe is normal, fill at slightly slower rate
-            THROTTLE_VERY_SLOW, // pipe is (nearly) full, fill slowly
-        } throttle;
-        avail -= written;
-        // FIXME cache these values to avoid re-computation
-        if (avail >= (mMaxFrames * 3) / 4) {
-            throttle = THROTTLE_VERY_FAST;
-        } else if (avail >= mMaxFrames / 2) {
-            throttle = THROTTLE_FAST;
-        } else if (avail >= (mMaxFrames * 3) / 8) {
-            throttle = THROTTLE_NOMINAL;
-        } else if (avail >= mMaxFrames / 4) {
-            throttle = THROTTLE_SLOW;
-        } else {
-            throttle = THROTTLE_VERY_SLOW;
-        }
+        // The throttle tries to keep the pipe about 11/16 full on average, with a slight jitter.
+        uint32_t ns;
         if (written > 0) {
-            // FIXME cache these values also
-            switch (throttle) {
-            case THROTTLE_VERY_FAST:
-            default:
+            size_t filled = (mMaxFrames - avail) + written;
+            // FIXME cache these values to avoid re-computation
+            if (filled <= mReqFrames / 4) {
+                // pipe is (nearly) empty, fill quickly
                 ns = written * ( 500000000 / Format_sampleRate(mFormat));
-                break;
-            case THROTTLE_FAST:
+            } else if (filled <= mReqFrames / 2) {
+                // pipe is normal, fill at slightly faster rate
                 ns = written * ( 750000000 / Format_sampleRate(mFormat));
-                break;
-            case THROTTLE_NOMINAL:
+            } else if (filled <= (mReqFrames * 5) / 8) {
+                // pipe is normal, fill at nominal rate
                 ns = written * (1000000000 / Format_sampleRate(mFormat));
-                break;
-            case THROTTLE_SLOW:
+            } else if (filled <= (mReqFrames * 3) / 4) {
+                // pipe is normal, fill at slightly slower rate
                 ns = written * (1100000000 / Format_sampleRate(mFormat));
-                break;
-            case THROTTLE_VERY_SLOW:
+            } else {
+                // pipe is (nearly) full, fill slowly
                 ns = written * (1250000000 / Format_sampleRate(mFormat));
-                break;
             }
         } else {
-            ns = mMaxFrames * (250000000 / Format_sampleRate(mFormat));
+            ns = mReqFrames * (250000000 / Format_sampleRate(mFormat));
         }
         if (ns > 999999999) {
             ns = 999999999;
diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h
index 545d6ac..aaaa51f 100644
--- a/services/audioflinger/MonoPipe.h
+++ b/services/audioflinger/MonoPipe.h
@@ -33,11 +33,11 @@
     friend class MonoPipeReader;
 
 public:
-    // maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
+    // reqFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
     // Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like
     // creating it the object before creating the other thread, or storing the object with a
     // release_store). Otherwise the other thread could see a partially-constructed object.
-    MonoPipe(size_t maxFrames, NBAIO_Format format, bool writeCanBlock = false);
+    MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false);
     virtual ~MonoPipe();
 
     // NBAIO_Port interface
@@ -58,9 +58,10 @@
 
             // average number of frames present in the pipe under normal conditions.
             // See throttling mechanism in MonoPipe::write()
-            size_t  getAvgFrames() const { return (mMaxFrames * 11) / 16; }
+            size_t  getAvgFrames() const { return (mReqFrames * 11) / 16; }
 
 private:
+    const size_t    mReqFrames;     // as requested in constructor, unrounded
     const size_t    mMaxFrames;     // always a power of 2
     void * const    mBuffer;
     // mFront and mRear will never be separated by more than mMaxFrames.