blob: fb489e27c2f11fdcd33503c4f8e43d4fe2d4799d [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 Kasten09acf782016-06-17 10:28:05 -0700106uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment)
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 Kasten09acf782016-06-17 10:28:05 -0700124int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost)
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 Kasten6d7ad762016-06-15 17:05:54 -0700188audio_utils_fifo_provider::audio_utils_fifo_provider() :
189 mObtained(0)
190{
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 Kasten3ddacbb2016-06-20 14:41:03 -0700200 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700201 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
202 mArmed(true), // because initial fill level of zero is < mLowLevelArm
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) {
351 if ((uint32_t) filled < mLowLevelArm) {
352 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700353 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700354 if (mArmed && filled + count > mHighLevelTrigger) {
355 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 }
362 mArmed = false;
363 }
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 Kasten547a9922016-06-15 13:07:31 -0700376 }
377}
378
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700379ssize_t audio_utils_fifo_writer::available()
380{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700381 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700382 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
383}
384
385void audio_utils_fifo_writer::resize(uint32_t frameCount)
386{
387 // cap to range [0, mFifo.mFrameCount]
388 if (frameCount > mFifo.mFrameCount) {
389 frameCount = mFifo.mFrameCount;
390 }
391 // if we reduce the effective frame count, update hysteresis points to be within the new range
392 if (frameCount < mEffectiveFrames) {
393 if (mLowLevelArm > frameCount) {
394 mLowLevelArm = frameCount;
395 }
396 if (mHighLevelTrigger > frameCount) {
397 mHighLevelTrigger = frameCount;
398 }
399 }
400 mEffectiveFrames = frameCount;
401}
402
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700403uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700404{
405 return mEffectiveFrames;
406}
407
408void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
409{
410 // cap to range [0, mEffectiveFrames]
411 if (lowLevelArm > mEffectiveFrames) {
412 lowLevelArm = mEffectiveFrames;
413 }
414 if (highLevelTrigger > mEffectiveFrames) {
415 highLevelTrigger = mEffectiveFrames;
416 }
417 // TODO this is overly conservative; it would be better to arm based on actual fill level
418 if (lowLevelArm > mLowLevelArm) {
419 mArmed = true;
420 }
421 mLowLevelArm = lowLevelArm;
422 mHighLevelTrigger = highLevelTrigger;
423}
424
425void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
426{
427 *lowLevelArm = mLowLevelArm;
428 *highLevelTrigger = mHighLevelTrigger;
429}
430
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700431////////////////////////////////////////////////////////////////////////////////
432
433audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700434 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
435 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700436 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
437 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700438{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700439}
440
441audio_utils_fifo_reader::~audio_utils_fifo_reader()
442{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700443 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700444}
445
Glenn Kastend9942f72016-10-16 14:51:15 -0700446ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700447 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700448 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800449{
Glenn Kasten547a9922016-06-15 13:07:31 -0700450 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700451 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700452 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700453 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
454 iovec[0].mLength * mFifo.mFrameSize);
455 if (iovec[1].mLength > 0) {
456 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
457 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
458 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700459 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700460 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700461 }
462 return availToRead;
463}
464
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700465ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700466 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700467 __attribute__((no_sanitize("integer")))
468{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700469 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700470}
471
472void audio_utils_fifo_reader::release(size_t count)
473 __attribute__((no_sanitize("integer")))
474{
475 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700476 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700477 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700478 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700479 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700480 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700481 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700482 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700483 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700484 // TODO add comments
485 int op = FUTEX_WAKE;
486 switch (mFifo.mThrottleFrontSync) {
487 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
488 break;
489 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
490 op = FUTEX_WAKE_PRIVATE;
491 // fall through
492 case AUDIO_UTILS_FIFO_SYNC_SHARED:
493 if (filled >= 0) {
494 if (filled > mHighLevelArm) {
495 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700496 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700497 if (mArmed && filled - count < mLowLevelTrigger) {
498 int err = sys_futex(&mThrottleFront->mIndex,
499 op, 1 /*waiters*/, NULL, NULL, 0);
500 // err is number of processes woken up
501 if (err < 0 || err > 1) {
502 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
503 __func__, err, errno);
504 }
505 mArmed = false;
506 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700507 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700508 break;
509 default:
510 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
511 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700512 }
513 } else {
514 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700515 }
516 mObtained -= count;
517 }
518}
519
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700520// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700521ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700522 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700523 __attribute__((no_sanitize("integer")))
524{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700525 int err = 0;
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700526 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700527 uint32_t rear;
528 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700529 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700530 std::memory_order_acquire);
531 // TODO pull out "count == 0"
532 if (count == 0 || rear != mLocalFront || timeout == NULL ||
533 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
534 break;
535 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700536 // TODO add comments
537 int op = FUTEX_WAIT;
538 switch (mFifo.mWriterRearSync) {
539 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
540 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
541 if (err < 0) {
542 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
543 err = -errno;
544 } else {
545 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700546 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700547 break;
548 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
549 op = FUTEX_WAIT_PRIVATE;
550 // fall through
551 case AUDIO_UTILS_FIFO_SYNC_SHARED:
552 if (timeout->tv_sec == LONG_MAX) {
553 timeout = NULL;
554 }
555 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
556 if (err < 0) {
557 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700558 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700559 // Benign race condition with partner: mFifo.mWriterRear->mIndex
560 // changed value between the earlier atomic_load_explicit() and sys_futex().
561 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700562 if (retries-- > 0) {
563 // bypass the "timeout = NULL;" below
564 continue;
565 }
566 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700567 case EINTR:
568 case ETIMEDOUT:
569 err = -errno;
570 break;
571 default:
572 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
573 break;
574 }
575 }
576 break;
577 default:
578 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
579 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700580 }
581 timeout = NULL;
582 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700583 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700584 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700585 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700586 mLocalFront = rear;
587 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700588 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700589 err = filled;
590 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700591 }
592 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800593 if (availToRead > count) {
594 availToRead = count;
595 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700596 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
597 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800598 if (part1 > availToRead) {
599 part1 = availToRead;
600 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700601 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700602 // return slice
603 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700604 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700605 iovec[0].mLength = part1;
606 iovec[1].mOffset = 0;
607 iovec[1].mLength = part2;
608 mObtained = availToRead;
609 }
610 return availToRead > 0 ? availToRead : err;
611}
612
613ssize_t audio_utils_fifo_reader::available()
614{
615 return available(NULL /*lost*/);
616}
617
618ssize_t audio_utils_fifo_reader::available(size_t *lost)
619{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700620 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700621 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
622}
623
624void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
625{
626 // cap to range [0, mFifo.mFrameCount]
627 if (highLevelArm < 0) {
628 highLevelArm = -1;
629 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
630 highLevelArm = mFifo.mFrameCount;
631 }
632 if (lowLevelTrigger > mFifo.mFrameCount) {
633 lowLevelTrigger = mFifo.mFrameCount;
634 }
635 // TODO this is overly conservative; it would be better to arm based on actual fill level
636 if (highLevelArm < mHighLevelArm) {
637 mArmed = true;
638 }
639 mHighLevelArm = highLevelArm;
640 mLowLevelTrigger = lowLevelTrigger;
641}
642
643void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
644{
645 *highLevelArm = mHighLevelArm;
646 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700647}