blob: 61bb1890c9f9ce58d75f027f178ddd49f4498fbe [file] [log] [blame]
Glenn Kasten9b4c8052015-01-06 14:13:13 -08001/*
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
Glenn Kasten9052f3b2016-07-08 16:24:41 -070020#include <errno.h>
Glenn Kasten0ab1d862016-06-20 12:04:56 -070021#include <limits.h>
Glenn Kasten9b4c8052015-01-06 14:13:13 -080022#include <stdlib.h>
23#include <string.h>
Glenn Kasten9052f3b2016-07-08 16:24:41 -070024
Glenn Kastenc0924bc2016-10-02 13:00:19 -070025// FIXME futex portion is not supported on macOS, should use the macOS alternative
Glenn Kasten9052f3b2016-07-08 16:24:41 -070026#ifdef __linux__
27#include <linux/futex.h>
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070028#include <sys/syscall.h>
Glenn Kasten9052f3b2016-07-08 16:24:41 -070029#else
30#define FUTEX_WAIT 0
31#define FUTEX_WAIT_PRIVATE 0
32#define FUTEX_WAKE 0
33#define FUTEX_WAKE_PRIVATE 0
34#endif
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070035
Glenn Kasten9b4c8052015-01-06 14:13:13 -080036#include <audio_utils/fifo.h>
37#include <audio_utils/roundup.h>
Glenn Kasten9b4c8052015-01-06 14:13:13 -080038#include <cutils/log.h>
Glenn Kasten9b4fe472016-06-13 09:34:57 -070039#include <utils/Errors.h>
Glenn Kasten9b4c8052015-01-06 14:13:13 -080040
Glenn Kastenc0924bc2016-10-02 13:00:19 -070041#ifdef __linux__
42#ifdef __ANDROID__
43// bionic for Android provides clock_nanosleep
44#else
45// bionic for desktop Linux omits clock_nanosleep
46int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request,
47 struct timespec *remain)
48{
49 return syscall(SYS_clock_nanosleep, clock_id, flags, request, remain);
50}
51#endif // __ANDROID__
52#else // __linux__
Dan Willemsen23f5bc22016-11-03 13:55:28 -070053// macOS <10.12 doesn't have clockid_t / CLOCK_MONOTONIC
54#ifndef CLOCK_MONOTONIC
Glenn Kastenc16f93d2016-10-14 10:05:26 -070055typedef int clockid_t;
56#define CLOCK_MONOTONIC 0
Dan Willemsen23f5bc22016-11-03 13:55:28 -070057#endif
58// macOS doesn't have clock_nanosleep
Glenn Kastenc0924bc2016-10-02 13:00:19 -070059int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request,
60 struct timespec *remain)
61{
Glenn Kastenc16f93d2016-10-14 10:05:26 -070062 (void) clock_id;
63 (void) flags;
64 (void) request;
65 (void) remain;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070066 errno = ENOSYS;
67 return -1;
68}
69#endif // __linux__
70
Glenn Kastend9942f72016-10-16 14:51:15 -070071static int sys_futex(void *addr1, int op, int val1, const struct timespec *timeout, void *addr2,
72 int val3)
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070073{
Glenn Kasten9052f3b2016-07-08 16:24:41 -070074#ifdef __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070075 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
Glenn Kastenc0924bc2016-10-02 13:00:19 -070076#else // __linux__
77 // macOS doesn't have futex
Glenn Kasten86c4a6d2016-07-09 10:37:17 -070078 (void) addr1;
79 (void) op;
80 (void) val1;
81 (void) timeout;
82 (void) addr2;
83 (void) val3;
Glenn Kasten9052f3b2016-07-08 16:24:41 -070084 errno = ENOSYS;
85 return -1;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070086#endif // __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070087}
88
Glenn Kastendc1ff1f2016-09-02 13:54:59 -070089audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
Glenn Kastenc0924bc2016-10-02 13:00:19 -070090 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten6d7ad762016-06-15 17:05:54 -070091 __attribute__((no_sanitize("integer"))) :
Glenn Kasten9b4fe472016-06-13 09:34:57 -070092 mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
Glenn Kasten09acf782016-06-17 10:28:05 -070093 mFudgeFactor(mFrameCountP2 - mFrameCount),
Glenn Kastenc0924bc2016-10-02 13:00:19 -070094 // FIXME need an API to configure the sync types
95 mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
Glenn Kasten0b2947b2016-11-22 14:00:23 -080096 mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
97 mIsShutdown(false)
Glenn Kasten9b4c8052015-01-06 14:13:13 -080098{
Glenn Kasten09acf782016-06-17 10:28:05 -070099 // actual upper bound on frameCount will depend on the frame size
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700100 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800101}
102
Glenn Kasten09acf782016-06-17 10:28:05 -0700103audio_utils_fifo_base::~audio_utils_fifo_base()
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800104{
105}
106
Glenn Kasten3f115ba2016-11-22 14:00:53 -0800107uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700108 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800109{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700110 if (mFudgeFactor) {
111 uint32_t mask = mFrameCountP2 - 1;
112 ALOG_ASSERT((index & mask) < mFrameCount);
113 ALOG_ASSERT(increment <= mFrameCountP2);
114 if ((index & mask) + increment >= mFrameCount) {
115 increment += mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800116 }
117 index += increment;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700118 ALOG_ASSERT((index & mask) < mFrameCount);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800119 return index;
120 } else {
121 return index + increment;
122 }
123}
124
Glenn Kasten3f115ba2016-11-22 14:00:53 -0800125int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) const
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700126 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800127{
Glenn Kasten44001d42016-10-19 15:46:02 -0700128 // TODO replace multiple returns by a single return point so this isn't needed
129 if (lost != NULL) {
130 *lost = 0;
131 }
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800132 if (mIsShutdown) {
133 return -EIO;
134 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700135 uint32_t diff = rear - front;
136 if (mFudgeFactor) {
137 uint32_t mask = mFrameCountP2 - 1;
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700138 uint32_t rearOffset = rear & mask;
139 uint32_t frontOffset = front & mask;
140 if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800141 ALOGE("%s frontOffset=%u rearOffset=%u mFrameCount=%u",
142 __func__, frontOffset, rearOffset, mFrameCount);
143 shutdown();
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700144 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700145 }
146 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800147 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700148 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700149 if (lost != NULL) {
150 // TODO provide a more accurate estimate
151 *lost = (genDiff / mFrameCountP2) * mFrameCount;
152 }
153 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700154 }
155 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800156 }
157 }
158 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700159 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700160 if (lost != NULL) {
Glenn Kasten44001d42016-10-19 15:46:02 -0700161 *lost = diff;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700162 }
163 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700164 }
165 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800166}
167
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800168void audio_utils_fifo_base::shutdown() const
169{
170 ALOGE("%s", __func__);
171 mIsShutdown = true;
172}
173
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700174////////////////////////////////////////////////////////////////////////////////
175
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700176audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700177 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700178 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700179 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700180 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700181{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700182 // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to
Glenn Kasten09acf782016-06-17 10:28:05 -0700183 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700184 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700185 frameCount > ((uint32_t) INT32_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700186}
187
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700188audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
189 bool throttlesWriter) :
190 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
191 throttlesWriter ? &mSingleProcessSharedFront : NULL)
192{
193}
194
Glenn Kasten09acf782016-06-17 10:28:05 -0700195audio_utils_fifo::~audio_utils_fifo()
196{
197}
198
199////////////////////////////////////////////////////////////////////////////////
200
Glenn Kasten0f850392016-10-19 12:14:46 -0700201audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
202 mFifo(fifo), mObtained(0), mTotalReleased(0)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700203{
204}
205
206audio_utils_fifo_provider::~audio_utils_fifo_provider()
207{
208}
209
210////////////////////////////////////////////////////////////////////////////////
211
212audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten0f850392016-10-19 12:14:46 -0700213 audio_utils_fifo_provider(fifo), mLocalRear(0),
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800214 mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
215 mIsArmed(true), // because initial fill level of zero is < mArmLevel
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700216 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700217{
218}
219
220audio_utils_fifo_writer::~audio_utils_fifo_writer()
221{
222}
223
Glenn Kastend9942f72016-10-16 14:51:15 -0700224ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
225 const struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700226 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800227{
Glenn Kasten547a9922016-06-15 13:07:31 -0700228 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700229 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700230 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700231 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
232 iovec[0].mLength * mFifo.mFrameSize);
233 if (iovec[1].mLength > 0) {
234 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
235 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
236 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700237 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700238 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700239 }
240 return availToWrite;
241}
242
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700243// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700244ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700245 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700246 __attribute__((no_sanitize("integer")))
247{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700248 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700249 size_t availToWrite;
250 if (mFifo.mThrottleFront != NULL) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700251 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700252 uint32_t front;
253 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700254 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800255 // returns -EIO if mIsShutdown
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700256 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700257 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700258 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700259 err = filled;
260 availToWrite = 0;
261 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700262 }
263 availToWrite = mEffectiveFrames > (uint32_t) filled ?
264 mEffectiveFrames - (uint32_t) filled : 0;
265 // TODO pull out "count == 0"
266 if (count == 0 || availToWrite > 0 || timeout == NULL ||
267 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
268 break;
269 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700270 // TODO add comments
271 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700272 // the high level code (synchronization, sleep, futex, iovec) should be completely
273 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700274 int op = FUTEX_WAIT;
275 switch (mFifo.mThrottleFrontSync) {
276 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
277 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
278 if (err < 0) {
279 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
280 err = -errno;
281 } else {
282 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700283 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700284 break;
285 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
286 op = FUTEX_WAIT_PRIVATE;
287 // fall through
288 case AUDIO_UTILS_FIFO_SYNC_SHARED:
289 if (timeout->tv_sec == LONG_MAX) {
290 timeout = NULL;
291 }
292 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
293 if (err < 0) {
294 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700295 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700296 // Benign race condition with partner: mFifo.mThrottleFront->mIndex
297 // changed value between the earlier atomic_load_explicit() and sys_futex().
298 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700299 if (retries-- > 0) {
300 // bypass the "timeout = NULL;" below
301 continue;
302 }
303 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700304 case EINTR:
305 case ETIMEDOUT:
306 err = -errno;
307 break;
308 default:
309 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
310 break;
311 }
312 }
313 break;
314 default:
315 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
316 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700317 }
318 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700319 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700320 } else {
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800321 if (mFifo.mIsShutdown) {
322 err = -EIO;
323 availToWrite = 0;
324 } else {
325 availToWrite = mEffectiveFrames;
326 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700327 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800328 if (availToWrite > count) {
329 availToWrite = count;
330 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700331 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
332 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800333 if (part1 > availToWrite) {
334 part1 = availToWrite;
335 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700336 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700337 // return slice
338 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700339 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700340 iovec[0].mLength = part1;
341 iovec[1].mOffset = 0;
342 iovec[1].mLength = part2;
343 mObtained = availToWrite;
344 }
345 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800346}
347
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700348void audio_utils_fifo_writer::release(size_t count)
349 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700350{
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800351 // no need to do an early check for mIsShutdown, because the extra code executed is harmless
Glenn Kasten547a9922016-06-15 13:07:31 -0700352 if (count > 0) {
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800353 if (count > mObtained) {
354 ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
355 mFifo.shutdown();
356 return;
357 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700358 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700359 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700360 std::memory_order_acquire);
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800361 // returns -EIO if mIsShutdown
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700362 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700363 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700364 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700365 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700366 // TODO add comments
367 int op = FUTEX_WAKE;
368 switch (mFifo.mWriterRearSync) {
369 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
370 break;
371 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
372 op = FUTEX_WAKE_PRIVATE;
373 // fall through
374 case AUDIO_UTILS_FIFO_SYNC_SHARED:
375 if (filled >= 0) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800376 if ((uint32_t) filled < mArmLevel) {
377 mIsArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700378 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800379 if (mIsArmed && filled + count > mTriggerLevel) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700380 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700381 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700382 // err is number of processes woken up
383 if (err < 0) {
384 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
385 __func__, err, errno);
386 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800387 mIsArmed = false;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700388 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700389 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700390 break;
391 default:
392 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
393 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700394 }
395 } else {
396 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700397 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700398 std::memory_order_release);
399 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700400 mObtained -= count;
Glenn Kasten0f850392016-10-19 12:14:46 -0700401 mTotalReleased += count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700402 }
403}
404
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700405ssize_t audio_utils_fifo_writer::available()
406{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700407 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700408 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
409}
410
411void audio_utils_fifo_writer::resize(uint32_t frameCount)
412{
413 // cap to range [0, mFifo.mFrameCount]
414 if (frameCount > mFifo.mFrameCount) {
415 frameCount = mFifo.mFrameCount;
416 }
417 // if we reduce the effective frame count, update hysteresis points to be within the new range
418 if (frameCount < mEffectiveFrames) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800419 if (mArmLevel > frameCount) {
420 mArmLevel = frameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700421 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800422 if (mTriggerLevel > frameCount) {
423 mTriggerLevel = frameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700424 }
425 }
426 mEffectiveFrames = frameCount;
427}
428
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700429uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700430{
431 return mEffectiveFrames;
432}
433
434void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
435{
436 // cap to range [0, mEffectiveFrames]
437 if (lowLevelArm > mEffectiveFrames) {
438 lowLevelArm = mEffectiveFrames;
439 }
440 if (highLevelTrigger > mEffectiveFrames) {
441 highLevelTrigger = mEffectiveFrames;
442 }
443 // TODO this is overly conservative; it would be better to arm based on actual fill level
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800444 if (lowLevelArm > mArmLevel) {
445 mIsArmed = true;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700446 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800447 mArmLevel = lowLevelArm;
448 mTriggerLevel = highLevelTrigger;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700449}
450
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800451void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700452{
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800453 *armLevel = mArmLevel;
454 *triggerLevel = mTriggerLevel;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700455}
456
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700457////////////////////////////////////////////////////////////////////////////////
458
459audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kasten0f850392016-10-19 12:14:46 -0700460 audio_utils_fifo_provider(fifo), mLocalFront(0),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700461 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800462 mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
463 mIsArmed(true), // because initial fill level of zero is > mArmLevel
Glenn Kasten0f850392016-10-19 12:14:46 -0700464 mTotalLost(0), mTotalFlushed(0)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700465{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700466}
467
468audio_utils_fifo_reader::~audio_utils_fifo_reader()
469{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700470 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700471}
472
Glenn Kastend9942f72016-10-16 14:51:15 -0700473ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700474 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700475 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800476{
Glenn Kasten547a9922016-06-15 13:07:31 -0700477 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700478 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700479 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700480 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
481 iovec[0].mLength * mFifo.mFrameSize);
482 if (iovec[1].mLength > 0) {
483 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
484 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
485 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700486 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700487 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700488 }
489 return availToRead;
490}
491
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700492ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700493 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700494 __attribute__((no_sanitize("integer")))
495{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700496 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700497}
498
499void audio_utils_fifo_reader::release(size_t count)
500 __attribute__((no_sanitize("integer")))
501{
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800502 // no need to do an early check for mIsShutdown, because the extra code executed is harmless
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700503 if (count > 0) {
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800504 if (count > mObtained) {
505 ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
506 mFifo.shutdown();
507 return;
508 }
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700509 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700510 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700511 std::memory_order_acquire);
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800512 // returns -EIO if mIsShutdown
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700513 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700514 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700515 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700516 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700517 // TODO add comments
518 int op = FUTEX_WAKE;
519 switch (mFifo.mThrottleFrontSync) {
520 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
521 break;
522 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
523 op = FUTEX_WAKE_PRIVATE;
524 // fall through
525 case AUDIO_UTILS_FIFO_SYNC_SHARED:
526 if (filled >= 0) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800527 if (filled > mArmLevel) {
528 mIsArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700529 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800530 if (mIsArmed && filled - count < mTriggerLevel) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700531 int err = sys_futex(&mThrottleFront->mIndex,
532 op, 1 /*waiters*/, NULL, NULL, 0);
533 // err is number of processes woken up
534 if (err < 0 || err > 1) {
535 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
536 __func__, err, errno);
537 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800538 mIsArmed = false;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700539 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700540 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700541 break;
542 default:
543 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
544 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700545 }
546 } else {
547 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700548 }
549 mObtained -= count;
Glenn Kasten0f850392016-10-19 12:14:46 -0700550 mTotalReleased += count;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700551 }
552}
553
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700554// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700555ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700556 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700557 __attribute__((no_sanitize("integer")))
558{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700559 int err = 0;
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700560 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700561 uint32_t rear;
562 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700563 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700564 std::memory_order_acquire);
565 // TODO pull out "count == 0"
566 if (count == 0 || rear != mLocalFront || timeout == NULL ||
567 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
568 break;
569 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700570 // TODO add comments
571 int op = FUTEX_WAIT;
572 switch (mFifo.mWriterRearSync) {
573 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
574 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
575 if (err < 0) {
576 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
577 err = -errno;
578 } else {
579 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700580 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700581 break;
582 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
583 op = FUTEX_WAIT_PRIVATE;
584 // fall through
585 case AUDIO_UTILS_FIFO_SYNC_SHARED:
586 if (timeout->tv_sec == LONG_MAX) {
587 timeout = NULL;
588 }
589 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
590 if (err < 0) {
591 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700592 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700593 // Benign race condition with partner: mFifo.mWriterRear->mIndex
594 // changed value between the earlier atomic_load_explicit() and sys_futex().
595 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700596 if (retries-- > 0) {
597 // bypass the "timeout = NULL;" below
598 continue;
599 }
600 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700601 case EINTR:
602 case ETIMEDOUT:
603 err = -errno;
604 break;
605 default:
606 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
607 break;
608 }
609 }
610 break;
611 default:
612 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
613 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700614 }
615 timeout = NULL;
616 }
Glenn Kasten0f850392016-10-19 12:14:46 -0700617 size_t ourLost;
618 if (lost == NULL) {
619 lost = &ourLost;
620 }
Glenn Kasten0b2947b2016-11-22 14:00:23 -0800621 // returns -EIO if mIsShutdown
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700622 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten0f850392016-10-19 12:14:46 -0700623 mTotalLost += *lost;
624 mTotalReleased += *lost;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700625 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700626 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700627 mLocalFront = rear;
628 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700629 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700630 err = filled;
631 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700632 }
633 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800634 if (availToRead > count) {
635 availToRead = count;
636 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700637 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
638 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800639 if (part1 > availToRead) {
640 part1 = availToRead;
641 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700642 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700643 // return slice
644 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700645 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700646 iovec[0].mLength = part1;
647 iovec[1].mOffset = 0;
648 iovec[1].mLength = part2;
649 mObtained = availToRead;
650 }
651 return availToRead > 0 ? availToRead : err;
652}
653
654ssize_t audio_utils_fifo_reader::available()
655{
656 return available(NULL /*lost*/);
657}
658
659ssize_t audio_utils_fifo_reader::available(size_t *lost)
660{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700661 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700662 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
663}
664
Glenn Kasten0f850392016-10-19 12:14:46 -0700665ssize_t audio_utils_fifo_reader::flush(size_t *lost)
666{
667 audio_utils_iovec iovec[2];
668 ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
669 if (ret > 0) {
670 size_t flushed = (size_t) ret;
671 release(flushed);
672 mTotalFlushed += flushed;
673 ret = flushed;
674 }
675 return ret;
676}
677
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800678void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700679{
680 // cap to range [0, mFifo.mFrameCount]
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800681 if (armLevel < 0) {
682 armLevel = -1;
683 } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
684 armLevel = mFifo.mFrameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700685 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800686 if (triggerLevel > mFifo.mFrameCount) {
687 triggerLevel = mFifo.mFrameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700688 }
689 // TODO this is overly conservative; it would be better to arm based on actual fill level
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800690 if (armLevel < mArmLevel) {
691 mIsArmed = true;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700692 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800693 mArmLevel = armLevel;
694 mTriggerLevel = triggerLevel;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700695}
696
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800697void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700698{
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800699 *armLevel = mArmLevel;
700 *triggerLevel = mTriggerLevel;
Glenn Kasten547a9922016-06-15 13:07:31 -0700701}