blob: 8a1ee6a2440f043ce2c8c2cd1a00d73e8b5ae583 [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),
96 mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED)
Glenn Kasten9b4c8052015-01-06 14:13:13 -080097{
Glenn Kasten09acf782016-06-17 10:28:05 -070098 // actual upper bound on frameCount will depend on the frame size
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -070099 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800100}
101
Glenn Kasten09acf782016-06-17 10:28:05 -0700102audio_utils_fifo_base::~audio_utils_fifo_base()
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800103{
104}
105
Glenn Kasten3f115ba2016-11-22 14:00:53 -0800106uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700107 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800108{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700109 if (mFudgeFactor) {
110 uint32_t mask = mFrameCountP2 - 1;
111 ALOG_ASSERT((index & mask) < mFrameCount);
112 ALOG_ASSERT(increment <= mFrameCountP2);
113 if ((index & mask) + increment >= mFrameCount) {
114 increment += mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800115 }
116 index += increment;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700117 ALOG_ASSERT((index & mask) < mFrameCount);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800118 return index;
119 } else {
120 return index + increment;
121 }
122}
123
Glenn Kasten3f115ba2016-11-22 14:00:53 -0800124int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) const
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700125 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800126{
Glenn Kasten44001d42016-10-19 15:46:02 -0700127 // TODO replace multiple returns by a single return point so this isn't needed
128 if (lost != NULL) {
129 *lost = 0;
130 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700131 uint32_t diff = rear - front;
132 if (mFudgeFactor) {
133 uint32_t mask = mFrameCountP2 - 1;
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700134 uint32_t rearOffset = rear & mask;
135 uint32_t frontOffset = front & mask;
136 if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700137 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700138 }
139 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800140 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700141 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700142 if (lost != NULL) {
143 // TODO provide a more accurate estimate
144 *lost = (genDiff / mFrameCountP2) * mFrameCount;
145 }
146 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700147 }
148 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800149 }
150 }
151 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700152 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700153 if (lost != NULL) {
Glenn Kasten44001d42016-10-19 15:46:02 -0700154 *lost = diff;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700155 }
156 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700157 }
158 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800159}
160
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700161////////////////////////////////////////////////////////////////////////////////
162
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700163audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700164 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700165 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700166 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700167 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700168{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700169 // 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 -0700170 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700171 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700172 frameCount > ((uint32_t) INT32_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700173}
174
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700175audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
176 bool throttlesWriter) :
177 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
178 throttlesWriter ? &mSingleProcessSharedFront : NULL)
179{
180}
181
Glenn Kasten09acf782016-06-17 10:28:05 -0700182audio_utils_fifo::~audio_utils_fifo()
183{
184}
185
186////////////////////////////////////////////////////////////////////////////////
187
Glenn Kasten0f850392016-10-19 12:14:46 -0700188audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
189 mFifo(fifo), mObtained(0), mTotalReleased(0)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700190{
191}
192
193audio_utils_fifo_provider::~audio_utils_fifo_provider()
194{
195}
196
197////////////////////////////////////////////////////////////////////////////////
198
199audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten0f850392016-10-19 12:14:46 -0700200 audio_utils_fifo_provider(fifo), mLocalRear(0),
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800201 mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
202 mIsArmed(true), // because initial fill level of zero is < mArmLevel
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700203 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700204{
205}
206
207audio_utils_fifo_writer::~audio_utils_fifo_writer()
208{
209}
210
Glenn Kastend9942f72016-10-16 14:51:15 -0700211ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
212 const struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700213 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800214{
Glenn Kasten547a9922016-06-15 13:07:31 -0700215 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700216 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700217 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700218 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
219 iovec[0].mLength * mFifo.mFrameSize);
220 if (iovec[1].mLength > 0) {
221 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
222 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
223 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700224 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700225 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700226 }
227 return availToWrite;
228}
229
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700230// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700231ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700232 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700233 __attribute__((no_sanitize("integer")))
234{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700235 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700236 size_t availToWrite;
237 if (mFifo.mThrottleFront != NULL) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700238 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700239 uint32_t front;
240 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700241 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
242 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700243 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700244 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700245 err = filled;
246 availToWrite = 0;
247 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700248 }
249 availToWrite = mEffectiveFrames > (uint32_t) filled ?
250 mEffectiveFrames - (uint32_t) filled : 0;
251 // TODO pull out "count == 0"
252 if (count == 0 || availToWrite > 0 || timeout == NULL ||
253 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
254 break;
255 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700256 // TODO add comments
257 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700258 // the high level code (synchronization, sleep, futex, iovec) should be completely
259 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700260 int op = FUTEX_WAIT;
261 switch (mFifo.mThrottleFrontSync) {
262 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
263 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
264 if (err < 0) {
265 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
266 err = -errno;
267 } else {
268 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700269 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700270 break;
271 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
272 op = FUTEX_WAIT_PRIVATE;
273 // fall through
274 case AUDIO_UTILS_FIFO_SYNC_SHARED:
275 if (timeout->tv_sec == LONG_MAX) {
276 timeout = NULL;
277 }
278 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
279 if (err < 0) {
280 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700281 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700282 // Benign race condition with partner: mFifo.mThrottleFront->mIndex
283 // changed value between the earlier atomic_load_explicit() and sys_futex().
284 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700285 if (retries-- > 0) {
286 // bypass the "timeout = NULL;" below
287 continue;
288 }
289 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700290 case EINTR:
291 case ETIMEDOUT:
292 err = -errno;
293 break;
294 default:
295 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
296 break;
297 }
298 }
299 break;
300 default:
301 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
302 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700303 }
304 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700305 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700306 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700307 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700308 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800309 if (availToWrite > count) {
310 availToWrite = count;
311 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700312 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
313 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800314 if (part1 > availToWrite) {
315 part1 = availToWrite;
316 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700317 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700318 // return slice
319 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700320 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700321 iovec[0].mLength = part1;
322 iovec[1].mOffset = 0;
323 iovec[1].mLength = part2;
324 mObtained = availToWrite;
325 }
326 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800327}
328
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700329void audio_utils_fifo_writer::release(size_t count)
330 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700331{
332 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700333 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700334 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700335 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700336 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700337 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700338 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700339 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700340 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700341 // TODO add comments
342 int op = FUTEX_WAKE;
343 switch (mFifo.mWriterRearSync) {
344 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
345 break;
346 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
347 op = FUTEX_WAKE_PRIVATE;
348 // fall through
349 case AUDIO_UTILS_FIFO_SYNC_SHARED:
350 if (filled >= 0) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800351 if ((uint32_t) filled < mArmLevel) {
352 mIsArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700353 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800354 if (mIsArmed && filled + count > mTriggerLevel) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700355 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700356 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700357 // err is number of processes woken up
358 if (err < 0) {
359 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
360 __func__, err, errno);
361 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800362 mIsArmed = false;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700363 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700364 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700365 break;
366 default:
367 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
368 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700369 }
370 } else {
371 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700372 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700373 std::memory_order_release);
374 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700375 mObtained -= count;
Glenn Kasten0f850392016-10-19 12:14:46 -0700376 mTotalReleased += count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700377 }
378}
379
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700380ssize_t audio_utils_fifo_writer::available()
381{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700382 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700383 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
384}
385
386void audio_utils_fifo_writer::resize(uint32_t frameCount)
387{
388 // cap to range [0, mFifo.mFrameCount]
389 if (frameCount > mFifo.mFrameCount) {
390 frameCount = mFifo.mFrameCount;
391 }
392 // if we reduce the effective frame count, update hysteresis points to be within the new range
393 if (frameCount < mEffectiveFrames) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800394 if (mArmLevel > frameCount) {
395 mArmLevel = frameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700396 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800397 if (mTriggerLevel > frameCount) {
398 mTriggerLevel = frameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700399 }
400 }
401 mEffectiveFrames = frameCount;
402}
403
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700404uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700405{
406 return mEffectiveFrames;
407}
408
409void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
410{
411 // cap to range [0, mEffectiveFrames]
412 if (lowLevelArm > mEffectiveFrames) {
413 lowLevelArm = mEffectiveFrames;
414 }
415 if (highLevelTrigger > mEffectiveFrames) {
416 highLevelTrigger = mEffectiveFrames;
417 }
418 // TODO this is overly conservative; it would be better to arm based on actual fill level
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800419 if (lowLevelArm > mArmLevel) {
420 mIsArmed = true;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700421 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800422 mArmLevel = lowLevelArm;
423 mTriggerLevel = highLevelTrigger;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700424}
425
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800426void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700427{
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800428 *armLevel = mArmLevel;
429 *triggerLevel = mTriggerLevel;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700430}
431
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700432////////////////////////////////////////////////////////////////////////////////
433
434audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kasten0f850392016-10-19 12:14:46 -0700435 audio_utils_fifo_provider(fifo), mLocalFront(0),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700436 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800437 mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
438 mIsArmed(true), // because initial fill level of zero is > mArmLevel
Glenn Kasten0f850392016-10-19 12:14:46 -0700439 mTotalLost(0), mTotalFlushed(0)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700440{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700441}
442
443audio_utils_fifo_reader::~audio_utils_fifo_reader()
444{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700445 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700446}
447
Glenn Kastend9942f72016-10-16 14:51:15 -0700448ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700449 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700450 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800451{
Glenn Kasten547a9922016-06-15 13:07:31 -0700452 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700453 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700454 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700455 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
456 iovec[0].mLength * mFifo.mFrameSize);
457 if (iovec[1].mLength > 0) {
458 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
459 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
460 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700461 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700462 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700463 }
464 return availToRead;
465}
466
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700467ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700468 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700469 __attribute__((no_sanitize("integer")))
470{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700471 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700472}
473
474void audio_utils_fifo_reader::release(size_t count)
475 __attribute__((no_sanitize("integer")))
476{
477 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700478 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700479 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700480 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700481 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700482 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700483 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700484 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700485 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700486 // TODO add comments
487 int op = FUTEX_WAKE;
488 switch (mFifo.mThrottleFrontSync) {
489 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
490 break;
491 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
492 op = FUTEX_WAKE_PRIVATE;
493 // fall through
494 case AUDIO_UTILS_FIFO_SYNC_SHARED:
495 if (filled >= 0) {
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800496 if (filled > mArmLevel) {
497 mIsArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700498 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800499 if (mIsArmed && filled - count < mTriggerLevel) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700500 int err = sys_futex(&mThrottleFront->mIndex,
501 op, 1 /*waiters*/, NULL, NULL, 0);
502 // err is number of processes woken up
503 if (err < 0 || err > 1) {
504 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
505 __func__, err, errno);
506 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800507 mIsArmed = false;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700508 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700509 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700510 break;
511 default:
512 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
513 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700514 }
515 } else {
516 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700517 }
518 mObtained -= count;
Glenn Kasten0f850392016-10-19 12:14:46 -0700519 mTotalReleased += count;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700520 }
521}
522
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700523// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700524ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700525 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700526 __attribute__((no_sanitize("integer")))
527{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700528 int err = 0;
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700529 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700530 uint32_t rear;
531 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700532 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700533 std::memory_order_acquire);
534 // TODO pull out "count == 0"
535 if (count == 0 || rear != mLocalFront || timeout == NULL ||
536 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
537 break;
538 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700539 // TODO add comments
540 int op = FUTEX_WAIT;
541 switch (mFifo.mWriterRearSync) {
542 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
543 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
544 if (err < 0) {
545 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
546 err = -errno;
547 } else {
548 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700549 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700550 break;
551 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
552 op = FUTEX_WAIT_PRIVATE;
553 // fall through
554 case AUDIO_UTILS_FIFO_SYNC_SHARED:
555 if (timeout->tv_sec == LONG_MAX) {
556 timeout = NULL;
557 }
558 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
559 if (err < 0) {
560 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700561 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700562 // Benign race condition with partner: mFifo.mWriterRear->mIndex
563 // changed value between the earlier atomic_load_explicit() and sys_futex().
564 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700565 if (retries-- > 0) {
566 // bypass the "timeout = NULL;" below
567 continue;
568 }
569 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700570 case EINTR:
571 case ETIMEDOUT:
572 err = -errno;
573 break;
574 default:
575 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
576 break;
577 }
578 }
579 break;
580 default:
581 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
582 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700583 }
584 timeout = NULL;
585 }
Glenn Kasten0f850392016-10-19 12:14:46 -0700586 size_t ourLost;
587 if (lost == NULL) {
588 lost = &ourLost;
589 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700590 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten0f850392016-10-19 12:14:46 -0700591 mTotalLost += *lost;
592 mTotalReleased += *lost;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700593 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700594 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700595 mLocalFront = rear;
596 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700597 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700598 err = filled;
599 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700600 }
601 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800602 if (availToRead > count) {
603 availToRead = count;
604 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700605 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
606 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800607 if (part1 > availToRead) {
608 part1 = availToRead;
609 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700610 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700611 // return slice
612 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700613 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700614 iovec[0].mLength = part1;
615 iovec[1].mOffset = 0;
616 iovec[1].mLength = part2;
617 mObtained = availToRead;
618 }
619 return availToRead > 0 ? availToRead : err;
620}
621
622ssize_t audio_utils_fifo_reader::available()
623{
624 return available(NULL /*lost*/);
625}
626
627ssize_t audio_utils_fifo_reader::available(size_t *lost)
628{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700629 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700630 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
631}
632
Glenn Kasten0f850392016-10-19 12:14:46 -0700633ssize_t audio_utils_fifo_reader::flush(size_t *lost)
634{
635 audio_utils_iovec iovec[2];
636 ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
637 if (ret > 0) {
638 size_t flushed = (size_t) ret;
639 release(flushed);
640 mTotalFlushed += flushed;
641 ret = flushed;
642 }
643 return ret;
644}
645
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800646void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700647{
648 // cap to range [0, mFifo.mFrameCount]
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800649 if (armLevel < 0) {
650 armLevel = -1;
651 } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
652 armLevel = mFifo.mFrameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700653 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800654 if (triggerLevel > mFifo.mFrameCount) {
655 triggerLevel = mFifo.mFrameCount;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700656 }
657 // TODO this is overly conservative; it would be better to arm based on actual fill level
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800658 if (armLevel < mArmLevel) {
659 mIsArmed = true;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700660 }
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800661 mArmLevel = armLevel;
662 mTriggerLevel = triggerLevel;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700663}
664
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800665void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700666{
Glenn Kastenb9652ab2016-11-18 13:34:01 -0800667 *armLevel = mArmLevel;
668 *triggerLevel = mTriggerLevel;
Glenn Kasten547a9922016-06-15 13:07:31 -0700669}