Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 820ba70d authored by Glenn Kasten's avatar Glenn Kasten
Browse files

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
parent 4364d2d1
Loading
Loading
Loading
Loading
+20 −38
Original line number Diff line number Diff line
@@ -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 @@ ssize_t MonoPipe::availableToWrite() const
    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 @@ ssize_t MonoPipe::write(const void *buffer, size_t count)
    }
    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 @@ ssize_t MonoPipe::write(const void *buffer, size_t count)
        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;
+4 −3
Original line number Diff line number Diff line
@@ -33,11 +33,11 @@ class MonoPipe : public NBAIO_Sink {
    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 @@ public:

            // 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.