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