fifo.cpp revision 8c5518ab1b5382bd047b5f16d8bd06e3c853705b
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "audio_utils_fifo"
19
20#include <errno.h>
21#include <limits.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include <audio_utils/clock_nanosleep.h>
26#include <audio_utils/fifo.h>
27#include <audio_utils/futex.h>
28#include <audio_utils/roundup.h>
29#include <cutils/log.h>
30#include <utils/Errors.h>
31
32audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
33        audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
34        __attribute__((no_sanitize("integer"))) :
35    mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
36    mFudgeFactor(mFrameCountP2 - mFrameCount),
37    // FIXME need an API to configure the sync types
38    mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
39    mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
40    mIsShutdown(false)
41{
42    // actual upper bound on frameCount will depend on the frame size
43    LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
44}
45
46audio_utils_fifo_base::~audio_utils_fifo_base()
47{
48}
49
50uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
51        __attribute__((no_sanitize("integer")))
52{
53    if (mFudgeFactor > 0) {
54        uint32_t mask = mFrameCountP2 - 1;
55        ALOG_ASSERT((index & mask) < mFrameCount);
56        ALOG_ASSERT(increment <= mFrameCountP2);
57        if ((index & mask) + increment >= mFrameCount) {
58            increment += mFudgeFactor;
59        }
60        index += increment;
61        ALOG_ASSERT((index & mask) < mFrameCount);
62        return index;
63    } else {
64        return index + increment;
65    }
66}
67
68int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost, bool flush) const
69        __attribute__((no_sanitize("integer")))
70{
71    // TODO replace multiple returns by a single return point so this isn't needed
72    if (lost != NULL) {
73        *lost = 0;
74    }
75    if (mIsShutdown) {
76        return -EIO;
77    }
78    uint32_t diff = rear - front;
79    if (mFudgeFactor > 0) {
80        uint32_t mask = mFrameCountP2 - 1;
81        uint32_t rearOffset = rear & mask;
82        uint32_t frontOffset = front & mask;
83        if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
84            ALOGE("%s frontOffset=%u rearOffset=%u mFrameCount=%u",
85                    __func__, frontOffset, rearOffset, mFrameCount);
86            shutdown();
87            return -EIO;
88        }
89        // genDiff is the difference between the generation count fields of rear and front,
90        // and is always a multiple of mFrameCountP2.
91        uint32_t genDiff = (rear & ~mask) - (front & ~mask);
92        // It's OK for writer to be one generation beyond reader,
93        // but reader has lost frames if writer is further than one generation beyond.
94        if (genDiff > mFrameCountP2) {
95            if (lost != NULL) {
96                // Calculate the number of lost frames as the raw difference,
97                // less the mFrameCount frames that are still valid and can be read on retry,
98                // less the wasted indices that don't count as true lost frames.
99                *lost = diff - (flush ? 0 : mFrameCount) - mFudgeFactor * (genDiff/mFrameCountP2);
100            }
101            return -EOVERFLOW;
102        }
103        // If writer is one generation beyond reader, skip over the wasted indices.
104        if (genDiff > 0) {
105            diff -= mFudgeFactor;
106            // Note is still possible for diff > mFrameCount. BCD 16 - BCD 1 shows the problem.
107            // genDiff is 16, fudge is 6, decimal diff is 15 = (22 - 1 - 6).
108            // So we need to check diff for overflow one more time. See "if" a few lines below.
109        }
110    }
111    // FIFO should not be overfull
112    if (diff > mFrameCount) {
113        if (lost != NULL) {
114            *lost = diff - (flush ? 0 : mFrameCount);
115        }
116        return -EOVERFLOW;
117    }
118    return (int32_t) diff;
119}
120
121void audio_utils_fifo_base::shutdown() const
122{
123    ALOGE("%s", __func__);
124    mIsShutdown = true;
125}
126
127////////////////////////////////////////////////////////////////////////////////
128
129audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
130        audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
131        __attribute__((no_sanitize("integer"))) :
132    audio_utils_fifo_base(frameCount, writerRear, throttleFront),
133    mFrameSize(frameSize), mBuffer(buffer)
134{
135    // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to
136    // be able to distinguish successful and error return values from read and write.
137    LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
138            frameCount > ((uint32_t) INT32_MAX) / frameSize);
139}
140
141audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
142        bool throttlesWriter) :
143    audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
144        throttlesWriter ?  &mSingleProcessSharedFront : NULL)
145{
146}
147
148audio_utils_fifo::~audio_utils_fifo()
149{
150}
151
152////////////////////////////////////////////////////////////////////////////////
153
154audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
155    mFifo(fifo), mObtained(0), mTotalReleased(0)
156{
157}
158
159audio_utils_fifo_provider::~audio_utils_fifo_provider()
160{
161}
162
163////////////////////////////////////////////////////////////////////////////////
164
165audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
166    audio_utils_fifo_provider(fifo), mLocalRear(0),
167    mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
168    mIsArmed(true), // because initial fill level of zero is < mArmLevel
169    mEffectiveFrames(fifo.mFrameCount)
170{
171}
172
173audio_utils_fifo_writer::~audio_utils_fifo_writer()
174{
175}
176
177ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
178        const struct timespec *timeout)
179        __attribute__((no_sanitize("integer")))
180{
181    audio_utils_iovec iovec[2];
182    ssize_t availToWrite = obtain(iovec, count, timeout);
183    if (availToWrite > 0) {
184        memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
185                iovec[0].mLength * mFifo.mFrameSize);
186        if (iovec[1].mLength > 0) {
187            memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
188                    (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
189                    iovec[1].mLength * mFifo.mFrameSize);
190        }
191        release(availToWrite);
192    }
193    return availToWrite;
194}
195
196// iovec == NULL is not part of the public API, but internally it means don't set mObtained
197ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
198        const struct timespec *timeout)
199        __attribute__((no_sanitize("integer")))
200{
201    int err = 0;
202    size_t availToWrite;
203    if (mFifo.mThrottleFront != NULL) {
204        int retries = kRetries;
205        uint32_t front;
206        for (;;) {
207            front = mFifo.mThrottleFront->loadAcquire();
208            // returns -EIO if mIsShutdown
209            int32_t filled = mFifo.diff(mLocalRear, front);
210            if (filled < 0) {
211                // on error, return an empty slice
212                err = filled;
213                availToWrite = 0;
214                break;
215            }
216            availToWrite = mEffectiveFrames > (uint32_t) filled ?
217                    mEffectiveFrames - (uint32_t) filled : 0;
218            // TODO pull out "count == 0"
219            if (count == 0 || availToWrite > 0 || timeout == NULL ||
220                    (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
221                break;
222            }
223            // TODO add comments
224            // TODO abstract out switch and replace by general sync object
225            //      the high level code (synchronization, sleep, futex, iovec) should be completely
226            //      separate from the low level code (indexes, available, masking).
227            int op = FUTEX_WAIT;
228            switch (mFifo.mThrottleFrontSync) {
229            case AUDIO_UTILS_FIFO_SYNC_SLEEP:
230                err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
231                        NULL /*remain*/);
232                if (err < 0) {
233                    LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
234                    err = -errno;
235                } else {
236                    err = -ETIMEDOUT;
237                }
238                break;
239            case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
240                op = FUTEX_WAIT_PRIVATE;
241                // fall through
242            case AUDIO_UTILS_FIFO_SYNC_SHARED:
243                if (timeout->tv_sec == LONG_MAX) {
244                    timeout = NULL;
245                }
246                err = mFifo.mThrottleFront->wait(op, front, timeout);
247                if (err < 0) {
248                    switch (errno) {
249                    case EWOULDBLOCK:
250                        // Benign race condition with partner: mFifo.mThrottleFront->mIndex
251                        // changed value between the earlier atomic_load_explicit() and sys_futex().
252                        // Try to load index again, but give up if we are unable to converge.
253                        if (retries-- > 0) {
254                            // bypass the "timeout = NULL;" below
255                            continue;
256                        }
257                        // fall through
258                    case EINTR:
259                    case ETIMEDOUT:
260                        err = -errno;
261                        break;
262                    default:
263                        LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
264                        break;
265                    }
266                }
267                break;
268            default:
269                LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
270                break;
271            }
272            timeout = NULL;
273        }
274    } else {
275        if (mFifo.mIsShutdown) {
276            err = -EIO;
277            availToWrite = 0;
278        } else {
279            availToWrite = mEffectiveFrames;
280        }
281    }
282    if (availToWrite > count) {
283        availToWrite = count;
284    }
285    uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
286    size_t part1 = mFifo.mFrameCount - rearOffset;
287    if (part1 > availToWrite) {
288        part1 = availToWrite;
289    }
290    size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
291    // return slice
292    if (iovec != NULL) {
293        iovec[0].mOffset = rearOffset;
294        iovec[0].mLength = part1;
295        iovec[1].mOffset = 0;
296        iovec[1].mLength = part2;
297        mObtained = availToWrite;
298    }
299    return availToWrite > 0 ? availToWrite : err;
300}
301
302void audio_utils_fifo_writer::release(size_t count)
303        __attribute__((no_sanitize("integer")))
304{
305    // no need to do an early check for mIsShutdown, because the extra code executed is harmless
306    if (count > 0) {
307        if (count > mObtained) {
308            ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
309            mFifo.shutdown();
310            return;
311        }
312        if (mFifo.mThrottleFront != NULL) {
313            uint32_t front = mFifo.mThrottleFront->loadAcquire();
314            // returns -EIO if mIsShutdown
315            int32_t filled = mFifo.diff(mLocalRear, front);
316            mLocalRear = mFifo.sum(mLocalRear, count);
317            mFifo.mWriterRear.storeRelease(mLocalRear);
318            // TODO add comments
319            int op = FUTEX_WAKE;
320            switch (mFifo.mWriterRearSync) {
321            case AUDIO_UTILS_FIFO_SYNC_SLEEP:
322                break;
323            case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
324                op = FUTEX_WAKE_PRIVATE;
325                // fall through
326            case AUDIO_UTILS_FIFO_SYNC_SHARED:
327                if (filled >= 0) {
328                    if ((uint32_t) filled < mArmLevel) {
329                        mIsArmed = true;
330                    }
331                    if (mIsArmed && filled + count > mTriggerLevel) {
332                        int err = mFifo.mWriterRear.wake(op, INT32_MAX /*waiters*/);
333                        // err is number of processes woken up
334                        if (err < 0) {
335                            LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
336                                    __func__, err, errno);
337                        }
338                        mIsArmed = false;
339                    }
340                }
341                break;
342            default:
343                LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
344                break;
345            }
346        } else {
347            mLocalRear = mFifo.sum(mLocalRear, count);
348            mFifo.mWriterRear.storeRelease(mLocalRear);
349        }
350        mObtained -= count;
351        mTotalReleased += count;
352    }
353}
354
355ssize_t audio_utils_fifo_writer::available()
356{
357    // iovec == NULL is not part of the public API, but internally it means don't set mObtained
358    return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
359}
360
361void audio_utils_fifo_writer::resize(uint32_t frameCount)
362{
363    // cap to range [0, mFifo.mFrameCount]
364    if (frameCount > mFifo.mFrameCount) {
365        frameCount = mFifo.mFrameCount;
366    }
367    // if we reduce the effective frame count, update hysteresis points to be within the new range
368    if (frameCount < mEffectiveFrames) {
369        if (mArmLevel > frameCount) {
370            mArmLevel = frameCount;
371        }
372        if (mTriggerLevel > frameCount) {
373            mTriggerLevel = frameCount;
374        }
375    }
376    mEffectiveFrames = frameCount;
377}
378
379uint32_t audio_utils_fifo_writer::size() const
380{
381    return mEffectiveFrames;
382}
383
384void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
385{
386    // cap to range [0, mEffectiveFrames]
387    if (lowLevelArm > mEffectiveFrames) {
388        lowLevelArm = mEffectiveFrames;
389    }
390    if (highLevelTrigger > mEffectiveFrames) {
391        highLevelTrigger = mEffectiveFrames;
392    }
393    // TODO this is overly conservative; it would be better to arm based on actual fill level
394    if (lowLevelArm > mArmLevel) {
395        mIsArmed = true;
396    }
397    mArmLevel = lowLevelArm;
398    mTriggerLevel = highLevelTrigger;
399}
400
401void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
402{
403    *armLevel = mArmLevel;
404    *triggerLevel = mTriggerLevel;
405}
406
407////////////////////////////////////////////////////////////////////////////////
408
409audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter,
410        bool flush) :
411    audio_utils_fifo_provider(fifo),
412
413    // If we throttle the writer, then initialize our front index to zero so that we see all data
414    // currently in the buffer.
415    // Otherwise, ignore everything currently in the buffer by initializing our front index to the
416    // current value of writer's rear.  This avoids an immediate -EOVERFLOW (overrun) in the case
417    // where reader starts out more than one buffer behind writer.  The initial catch-up does not
418    // contribute towards the totalLost, totalFlushed, or totalReleased counters.
419    mLocalFront(throttlesWriter ? 0 : mFifo.mWriterRear.loadConsume()),
420
421    mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
422    mFlush(flush),
423    mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
424    mIsArmed(true), // because initial fill level of zero is > mArmLevel
425    mTotalLost(0), mTotalFlushed(0)
426{
427}
428
429audio_utils_fifo_reader::~audio_utils_fifo_reader()
430{
431    // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
432}
433
434ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
435        size_t *lost)
436        __attribute__((no_sanitize("integer")))
437{
438    audio_utils_iovec iovec[2];
439    ssize_t availToRead = obtain(iovec, count, timeout, lost);
440    if (availToRead > 0) {
441        memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
442                iovec[0].mLength * mFifo.mFrameSize);
443        if (iovec[1].mLength > 0) {
444            memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
445                    (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
446                    iovec[1].mLength * mFifo.mFrameSize);
447        }
448        release(availToRead);
449    }
450    return availToRead;
451}
452
453ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
454        const struct timespec *timeout)
455        __attribute__((no_sanitize("integer")))
456{
457    return obtain(iovec, count, timeout, NULL /*lost*/);
458}
459
460void audio_utils_fifo_reader::release(size_t count)
461        __attribute__((no_sanitize("integer")))
462{
463    // no need to do an early check for mIsShutdown, because the extra code executed is harmless
464    if (count > 0) {
465        if (count > mObtained) {
466            ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
467            mFifo.shutdown();
468            return;
469        }
470        if (mThrottleFront != NULL) {
471            uint32_t rear = mFifo.mWriterRear.loadAcquire();
472            // returns -EIO if mIsShutdown
473            int32_t filled = mFifo.diff(rear, mLocalFront);
474            mLocalFront = mFifo.sum(mLocalFront, count);
475            mThrottleFront->storeRelease(mLocalFront);
476            // TODO add comments
477            int op = FUTEX_WAKE;
478            switch (mFifo.mThrottleFrontSync) {
479            case AUDIO_UTILS_FIFO_SYNC_SLEEP:
480                break;
481            case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
482                op = FUTEX_WAKE_PRIVATE;
483                // fall through
484            case AUDIO_UTILS_FIFO_SYNC_SHARED:
485                if (filled >= 0) {
486                    if (filled > mArmLevel) {
487                        mIsArmed = true;
488                    }
489                    if (mIsArmed && filled - count < mTriggerLevel) {
490                        int err = mThrottleFront->wake(op, 1 /*waiters*/);
491                        // err is number of processes woken up
492                        if (err < 0 || err > 1) {
493                            LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
494                                    __func__, err, errno);
495                        }
496                        mIsArmed = false;
497                    }
498                }
499                break;
500            default:
501                LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
502                break;
503            }
504        } else {
505            mLocalFront = mFifo.sum(mLocalFront, count);
506        }
507        mObtained -= count;
508        mTotalReleased += count;
509    }
510}
511
512// iovec == NULL is not part of the public API, but internally it means don't set mObtained
513ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
514        const struct timespec *timeout, size_t *lost)
515        __attribute__((no_sanitize("integer")))
516{
517    int err = 0;
518    int retries = kRetries;
519    uint32_t rear;
520    for (;;) {
521        rear = mFifo.mWriterRear.loadAcquire();
522        // TODO pull out "count == 0"
523        if (count == 0 || rear != mLocalFront || timeout == NULL ||
524                (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
525            break;
526        }
527        // TODO add comments
528        int op = FUTEX_WAIT;
529        switch (mFifo.mWriterRearSync) {
530        case AUDIO_UTILS_FIFO_SYNC_SLEEP:
531            err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
532                    NULL /*remain*/);
533            if (err < 0) {
534                LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
535                err = -errno;
536            } else {
537                err = -ETIMEDOUT;
538            }
539            break;
540        case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
541            op = FUTEX_WAIT_PRIVATE;
542            // fall through
543        case AUDIO_UTILS_FIFO_SYNC_SHARED:
544            if (timeout->tv_sec == LONG_MAX) {
545                timeout = NULL;
546            }
547            err = mFifo.mWriterRear.wait(op, rear, timeout);
548            if (err < 0) {
549                switch (errno) {
550                case EWOULDBLOCK:
551                    // Benign race condition with partner: mFifo.mWriterRear->mIndex
552                    // changed value between the earlier atomic_load_explicit() and sys_futex().
553                    // Try to load index again, but give up if we are unable to converge.
554                    if (retries-- > 0) {
555                        // bypass the "timeout = NULL;" below
556                        continue;
557                    }
558                    // fall through
559                case EINTR:
560                case ETIMEDOUT:
561                    err = -errno;
562                    break;
563                default:
564                    LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
565                    break;
566                }
567            }
568            break;
569        default:
570            LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
571            break;
572        }
573        timeout = NULL;
574    }
575    size_t ourLost;
576    if (lost == NULL) {
577        lost = &ourLost;
578    }
579    // returns -EIO if mIsShutdown
580    int32_t filled = mFifo.diff(rear, mLocalFront, lost, mFlush);
581    mTotalLost += *lost;
582    mTotalReleased += *lost;
583    if (filled < 0) {
584        if (filled == -EOVERFLOW) {
585            // catch up with writer, but preserve the still valid frames in buffer
586            mLocalFront = rear - (mFlush ? 0 : mFifo.mFrameCountP2 /*sic*/);
587        }
588        // on error, return an empty slice
589        err = filled;
590        filled = 0;
591    }
592    size_t availToRead = (size_t) filled;
593    if (availToRead > count) {
594        availToRead = count;
595    }
596    uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
597    size_t part1 = mFifo.mFrameCount - frontOffset;
598    if (part1 > availToRead) {
599        part1 = availToRead;
600    }
601    size_t part2 = part1 > 0 ? availToRead - part1 : 0;
602    // return slice
603    if (iovec != NULL) {
604        iovec[0].mOffset = frontOffset;
605        iovec[0].mLength = part1;
606        iovec[1].mOffset = 0;
607        iovec[1].mLength = part2;
608        mObtained = availToRead;
609    }
610    return availToRead > 0 ? availToRead : err;
611}
612
613ssize_t audio_utils_fifo_reader::available()
614{
615    return available(NULL /*lost*/);
616}
617
618ssize_t audio_utils_fifo_reader::available(size_t *lost)
619{
620    // iovec == NULL is not part of the public API, but internally it means don't set mObtained
621    return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
622}
623
624ssize_t audio_utils_fifo_reader::flush(size_t *lost)
625{
626    audio_utils_iovec iovec[2];
627    ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
628    if (ret > 0) {
629        size_t flushed = (size_t) ret;
630        release(flushed);
631        mTotalFlushed += flushed;
632        ret = flushed;
633    }
634    return ret;
635}
636
637void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
638{
639    // cap to range [0, mFifo.mFrameCount]
640    if (armLevel < 0) {
641        armLevel = -1;
642    } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
643        armLevel = mFifo.mFrameCount;
644    }
645    if (triggerLevel > mFifo.mFrameCount) {
646        triggerLevel = mFifo.mFrameCount;
647    }
648    // TODO this is overly conservative; it would be better to arm based on actual fill level
649    if (armLevel < mArmLevel) {
650        mIsArmed = true;
651    }
652    mArmLevel = armLevel;
653    mTriggerLevel = triggerLevel;
654}
655
656void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
657{
658    *armLevel = mArmLevel;
659    *triggerLevel = mTriggerLevel;
660}
661