2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "audio_utils_fifo"
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>
32 audio_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),
42 // actual upper bound on frameCount will depend on the frame size
43 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
46 audio_utils_fifo_base::~audio_utils_fifo_base()
50 uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
51 __attribute__((no_sanitize("integer")))
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;
61 ALOG_ASSERT((index & mask) < mFrameCount);
64 return index + increment;
68 int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) const
69 __attribute__((no_sanitize("integer")))
71 // TODO replace multiple returns by a single return point so this isn't needed
78 uint32_t diff = rear - front;
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);
89 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
91 if (genDiff > mFrameCountP2) {
93 // TODO provide a more accurate estimate
94 *lost = (genDiff / mFrameCountP2) * mFrameCount;
101 // FIFO should not be overfull
102 if (diff > mFrameCount) {
108 return (int32_t) diff;
111 void audio_utils_fifo_base::shutdown() const
113 ALOGE("%s", __func__);
117 ////////////////////////////////////////////////////////////////////////////////
119 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
120 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
121 __attribute__((no_sanitize("integer"))) :
122 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
123 mFrameSize(frameSize), mBuffer(buffer)
125 // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to
126 // be able to distinguish successful and error return values from read and write.
127 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
128 frameCount > ((uint32_t) INT32_MAX) / frameSize);
131 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
132 bool throttlesWriter) :
133 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
134 throttlesWriter ? &mSingleProcessSharedFront : NULL)
138 audio_utils_fifo::~audio_utils_fifo()
142 ////////////////////////////////////////////////////////////////////////////////
144 audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
145 mFifo(fifo), mObtained(0), mTotalReleased(0)
149 audio_utils_fifo_provider::~audio_utils_fifo_provider()
153 ////////////////////////////////////////////////////////////////////////////////
155 audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
156 audio_utils_fifo_provider(fifo), mLocalRear(0),
157 mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
158 mIsArmed(true), // because initial fill level of zero is < mArmLevel
159 mEffectiveFrames(fifo.mFrameCount)
163 audio_utils_fifo_writer::~audio_utils_fifo_writer()
167 ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
168 const struct timespec *timeout)
169 __attribute__((no_sanitize("integer")))
171 audio_utils_iovec iovec[2];
172 ssize_t availToWrite = obtain(iovec, count, timeout);
173 if (availToWrite > 0) {
174 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
175 iovec[0].mLength * mFifo.mFrameSize);
176 if (iovec[1].mLength > 0) {
177 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
178 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
179 iovec[1].mLength * mFifo.mFrameSize);
181 release(availToWrite);
186 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
187 ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
188 const struct timespec *timeout)
189 __attribute__((no_sanitize("integer")))
193 if (mFifo.mThrottleFront != NULL) {
194 int retries = kRetries;
197 front = mFifo.mThrottleFront->loadAcquire();
198 // returns -EIO if mIsShutdown
199 int32_t filled = mFifo.diff(mLocalRear, front);
201 // on error, return an empty slice
206 availToWrite = mEffectiveFrames > (uint32_t) filled ?
207 mEffectiveFrames - (uint32_t) filled : 0;
208 // TODO pull out "count == 0"
209 if (count == 0 || availToWrite > 0 || timeout == NULL ||
210 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
214 // TODO abstract out switch and replace by general sync object
215 // the high level code (synchronization, sleep, futex, iovec) should be completely
216 // separate from the low level code (indexes, available, masking).
218 switch (mFifo.mThrottleFrontSync) {
219 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
220 err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
223 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
229 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
230 op = FUTEX_WAIT_PRIVATE;
232 case AUDIO_UTILS_FIFO_SYNC_SHARED:
233 if (timeout->tv_sec == LONG_MAX) {
236 err = mFifo.mThrottleFront->wait(op, front, timeout);
240 // Benign race condition with partner: mFifo.mThrottleFront->mIndex
241 // changed value between the earlier atomic_load_explicit() and sys_futex().
242 // Try to load index again, but give up if we are unable to converge.
244 // bypass the "timeout = NULL;" below
253 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
259 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
265 if (mFifo.mIsShutdown) {
269 availToWrite = mEffectiveFrames;
272 if (availToWrite > count) {
273 availToWrite = count;
275 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
276 size_t part1 = mFifo.mFrameCount - rearOffset;
277 if (part1 > availToWrite) {
278 part1 = availToWrite;
280 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
283 iovec[0].mOffset = rearOffset;
284 iovec[0].mLength = part1;
285 iovec[1].mOffset = 0;
286 iovec[1].mLength = part2;
287 mObtained = availToWrite;
289 return availToWrite > 0 ? availToWrite : err;
292 void audio_utils_fifo_writer::release(size_t count)
293 __attribute__((no_sanitize("integer")))
295 // no need to do an early check for mIsShutdown, because the extra code executed is harmless
297 if (count > mObtained) {
298 ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
302 if (mFifo.mThrottleFront != NULL) {
303 uint32_t front = mFifo.mThrottleFront->loadAcquire();
304 // returns -EIO if mIsShutdown
305 int32_t filled = mFifo.diff(mLocalRear, front);
306 mLocalRear = mFifo.sum(mLocalRear, count);
307 mFifo.mWriterRear.storeRelease(mLocalRear);
310 switch (mFifo.mWriterRearSync) {
311 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
313 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
314 op = FUTEX_WAKE_PRIVATE;
316 case AUDIO_UTILS_FIFO_SYNC_SHARED:
318 if ((uint32_t) filled < mArmLevel) {
321 if (mIsArmed && filled + count > mTriggerLevel) {
322 int err = mFifo.mWriterRear.wake(op, INT32_MAX /*waiters*/);
323 // err is number of processes woken up
325 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
326 __func__, err, errno);
333 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
337 mLocalRear = mFifo.sum(mLocalRear, count);
338 mFifo.mWriterRear.storeRelease(mLocalRear);
341 mTotalReleased += count;
345 ssize_t audio_utils_fifo_writer::available()
347 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
348 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
351 void audio_utils_fifo_writer::resize(uint32_t frameCount)
353 // cap to range [0, mFifo.mFrameCount]
354 if (frameCount > mFifo.mFrameCount) {
355 frameCount = mFifo.mFrameCount;
357 // if we reduce the effective frame count, update hysteresis points to be within the new range
358 if (frameCount < mEffectiveFrames) {
359 if (mArmLevel > frameCount) {
360 mArmLevel = frameCount;
362 if (mTriggerLevel > frameCount) {
363 mTriggerLevel = frameCount;
366 mEffectiveFrames = frameCount;
369 uint32_t audio_utils_fifo_writer::size() const
371 return mEffectiveFrames;
374 void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
376 // cap to range [0, mEffectiveFrames]
377 if (lowLevelArm > mEffectiveFrames) {
378 lowLevelArm = mEffectiveFrames;
380 if (highLevelTrigger > mEffectiveFrames) {
381 highLevelTrigger = mEffectiveFrames;
383 // TODO this is overly conservative; it would be better to arm based on actual fill level
384 if (lowLevelArm > mArmLevel) {
387 mArmLevel = lowLevelArm;
388 mTriggerLevel = highLevelTrigger;
391 void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
393 *armLevel = mArmLevel;
394 *triggerLevel = mTriggerLevel;
397 ////////////////////////////////////////////////////////////////////////////////
399 audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
400 audio_utils_fifo_provider(fifo), mLocalFront(0),
401 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
402 mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
403 mIsArmed(true), // because initial fill level of zero is > mArmLevel
404 mTotalLost(0), mTotalFlushed(0)
408 audio_utils_fifo_reader::~audio_utils_fifo_reader()
410 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
413 ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
415 __attribute__((no_sanitize("integer")))
417 audio_utils_iovec iovec[2];
418 ssize_t availToRead = obtain(iovec, count, timeout, lost);
419 if (availToRead > 0) {
420 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
421 iovec[0].mLength * mFifo.mFrameSize);
422 if (iovec[1].mLength > 0) {
423 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
424 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
425 iovec[1].mLength * mFifo.mFrameSize);
427 release(availToRead);
432 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
433 const struct timespec *timeout)
434 __attribute__((no_sanitize("integer")))
436 return obtain(iovec, count, timeout, NULL /*lost*/);
439 void audio_utils_fifo_reader::release(size_t count)
440 __attribute__((no_sanitize("integer")))
442 // no need to do an early check for mIsShutdown, because the extra code executed is harmless
444 if (count > mObtained) {
445 ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
449 if (mThrottleFront != NULL) {
450 uint32_t rear = mFifo.mWriterRear.loadAcquire();
451 // returns -EIO if mIsShutdown
452 int32_t filled = mFifo.diff(rear, mLocalFront);
453 mLocalFront = mFifo.sum(mLocalFront, count);
454 mThrottleFront->storeRelease(mLocalFront);
457 switch (mFifo.mThrottleFrontSync) {
458 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
460 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
461 op = FUTEX_WAKE_PRIVATE;
463 case AUDIO_UTILS_FIFO_SYNC_SHARED:
465 if (filled > mArmLevel) {
468 if (mIsArmed && filled - count < mTriggerLevel) {
469 int err = mThrottleFront->wake(op, 1 /*waiters*/);
470 // err is number of processes woken up
471 if (err < 0 || err > 1) {
472 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
473 __func__, err, errno);
480 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
484 mLocalFront = mFifo.sum(mLocalFront, count);
487 mTotalReleased += count;
491 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
492 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
493 const struct timespec *timeout, size_t *lost)
494 __attribute__((no_sanitize("integer")))
497 int retries = kRetries;
500 rear = mFifo.mWriterRear.loadAcquire();
501 // TODO pull out "count == 0"
502 if (count == 0 || rear != mLocalFront || timeout == NULL ||
503 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
508 switch (mFifo.mWriterRearSync) {
509 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
510 err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
513 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
519 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
520 op = FUTEX_WAIT_PRIVATE;
522 case AUDIO_UTILS_FIFO_SYNC_SHARED:
523 if (timeout->tv_sec == LONG_MAX) {
526 err = mFifo.mWriterRear.wait(op, rear, timeout);
530 // Benign race condition with partner: mFifo.mWriterRear->mIndex
531 // changed value between the earlier atomic_load_explicit() and sys_futex().
532 // Try to load index again, but give up if we are unable to converge.
534 // bypass the "timeout = NULL;" below
543 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
549 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
558 // returns -EIO if mIsShutdown
559 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
561 mTotalReleased += *lost;
563 if (filled == -EOVERFLOW) {
566 // on error, return an empty slice
570 size_t availToRead = (size_t) filled;
571 if (availToRead > count) {
574 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
575 size_t part1 = mFifo.mFrameCount - frontOffset;
576 if (part1 > availToRead) {
579 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
582 iovec[0].mOffset = frontOffset;
583 iovec[0].mLength = part1;
584 iovec[1].mOffset = 0;
585 iovec[1].mLength = part2;
586 mObtained = availToRead;
588 return availToRead > 0 ? availToRead : err;
591 ssize_t audio_utils_fifo_reader::available()
593 return available(NULL /*lost*/);
596 ssize_t audio_utils_fifo_reader::available(size_t *lost)
598 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
599 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
602 ssize_t audio_utils_fifo_reader::flush(size_t *lost)
604 audio_utils_iovec iovec[2];
605 ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
607 size_t flushed = (size_t) ret;
609 mTotalFlushed += flushed;
615 void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
617 // cap to range [0, mFifo.mFrameCount]
620 } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
621 armLevel = mFifo.mFrameCount;
623 if (triggerLevel > mFifo.mFrameCount) {
624 triggerLevel = mFifo.mFrameCount;
626 // TODO this is overly conservative; it would be better to arm based on actual fill level
627 if (armLevel < mArmLevel) {
630 mArmLevel = armLevel;
631 mTriggerLevel = triggerLevel;
634 void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
636 *armLevel = mArmLevel;
637 *triggerLevel = mTriggerLevel;