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
audio_utils_fifo_base(uint32_t frameCount,audio_utils_fifo_index & writerRear,audio_utils_fifo_index * throttleFront,audio_utils_fifo_sync sync)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
~audio_utils_fifo_base()48 audio_utils_fifo_base::~audio_utils_fifo_base()
49 {
50 }
51
sum(uint32_t index,uint32_t increment) const52 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
diff(uint32_t rear,uint32_t front,size_t * lost,bool flush) const70 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
shutdown() const123 void audio_utils_fifo_base::shutdown() const
124 {
125 ALOGE("%s", __func__);
126 mIsShutdown = true;
127 }
128
129 ////////////////////////////////////////////////////////////////////////////////
130
audio_utils_fifo(uint32_t frameCount,uint32_t frameSize,void * buffer,audio_utils_fifo_index & writerRear,audio_utils_fifo_index * throttleFront)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
audio_utils_fifo(uint32_t frameCount,uint32_t frameSize,void * buffer,bool throttlesWriter,audio_utils_fifo_sync sync)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
~audio_utils_fifo()151 audio_utils_fifo::~audio_utils_fifo()
152 {
153 }
154
155 ////////////////////////////////////////////////////////////////////////////////
156
audio_utils_fifo_provider(audio_utils_fifo & fifo)157 audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
158 mFifo(fifo), mObtained(0), mTotalReleased(0)
159 {
160 }
161
~audio_utils_fifo_provider()162 audio_utils_fifo_provider::~audio_utils_fifo_provider()
163 {
164 }
165
166 ////////////////////////////////////////////////////////////////////////////////
167
audio_utils_fifo_writer(audio_utils_fifo & fifo)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
~audio_utils_fifo_writer()176 audio_utils_fifo_writer::~audio_utils_fifo_writer()
177 {
178 }
179
write(const void * buffer,size_t count,const struct timespec * timeout)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
obtain(audio_utils_iovec iovec[2],size_t count,const struct timespec * timeout)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
release(size_t count)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
available()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
resize(uint32_t frameCount)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
size() const397 uint32_t audio_utils_fifo_writer::size() const
398 {
399 return mEffectiveFrames;
400 }
401
setHysteresis(uint32_t lowLevelArm,uint32_t highLevelTrigger)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
getHysteresis(uint32_t * armLevel,uint32_t * triggerLevel) const419 void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
420 {
421 *armLevel = mArmLevel;
422 *triggerLevel = mTriggerLevel;
423 }
424
425 ////////////////////////////////////////////////////////////////////////////////
426
audio_utils_fifo_reader(audio_utils_fifo & fifo,bool throttlesWriter,bool flush)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
~audio_utils_fifo_reader()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
read(void * buffer,size_t count,const struct timespec * timeout,size_t * lost)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
obtain(audio_utils_iovec iovec[2],size_t count,const struct timespec * timeout)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
release(size_t count)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
obtain(audio_utils_iovec iovec[2],size_t count,const struct timespec * timeout,size_t * lost)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
available()641 ssize_t audio_utils_fifo_reader::available()
642 {
643 return available(NULL /*lost*/);
644 }
645
available(size_t * lost)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
flush(size_t * lost)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
setHysteresis(int32_t armLevel,uint32_t triggerLevel)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
getHysteresis(int32_t * armLevel,uint32_t * triggerLevel) const684 void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
685 {
686 *armLevel = mArmLevel;
687 *triggerLevel = mTriggerLevel;
688 }
689