blob: 325445a9da329f73fa5ec523749e1077ed8d230d [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__
53// macOS doesn't have clock_nanosleep
Glenn Kastenc16f93d2016-10-14 10:05:26 -070054typedef int clockid_t;
55#define CLOCK_MONOTONIC 0
Glenn Kastenc0924bc2016-10-02 13:00:19 -070056int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request,
57 struct timespec *remain)
58{
Glenn Kastenc16f93d2016-10-14 10:05:26 -070059 (void) clock_id;
60 (void) flags;
61 (void) request;
62 (void) remain;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070063 errno = ENOSYS;
64 return -1;
65}
66#endif // __linux__
67
Glenn Kastend9942f72016-10-16 14:51:15 -070068static int sys_futex(void *addr1, int op, int val1, const struct timespec *timeout, void *addr2,
69 int val3)
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070070{
Glenn Kasten9052f3b2016-07-08 16:24:41 -070071#ifdef __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070072 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
Glenn Kastenc0924bc2016-10-02 13:00:19 -070073#else // __linux__
74 // macOS doesn't have futex
Glenn Kasten86c4a6d2016-07-09 10:37:17 -070075 (void) addr1;
76 (void) op;
77 (void) val1;
78 (void) timeout;
79 (void) addr2;
80 (void) val3;
Glenn Kasten9052f3b2016-07-08 16:24:41 -070081 errno = ENOSYS;
82 return -1;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070083#endif // __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070084}
85
Glenn Kastendc1ff1f2016-09-02 13:54:59 -070086audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
Glenn Kastenc0924bc2016-10-02 13:00:19 -070087 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten6d7ad762016-06-15 17:05:54 -070088 __attribute__((no_sanitize("integer"))) :
Glenn Kasten9b4fe472016-06-13 09:34:57 -070089 mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
Glenn Kasten09acf782016-06-17 10:28:05 -070090 mFudgeFactor(mFrameCountP2 - mFrameCount),
Glenn Kastenc0924bc2016-10-02 13:00:19 -070091 // FIXME need an API to configure the sync types
92 mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
93 mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED)
Glenn Kasten9b4c8052015-01-06 14:13:13 -080094{
Glenn Kasten09acf782016-06-17 10:28:05 -070095 // actual upper bound on frameCount will depend on the frame size
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -070096 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
Glenn Kasten9b4c8052015-01-06 14:13:13 -080097}
98
Glenn Kasten09acf782016-06-17 10:28:05 -070099audio_utils_fifo_base::~audio_utils_fifo_base()
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800100{
101}
102
Glenn Kasten09acf782016-06-17 10:28:05 -0700103uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700104 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800105{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700106 if (mFudgeFactor) {
107 uint32_t mask = mFrameCountP2 - 1;
108 ALOG_ASSERT((index & mask) < mFrameCount);
109 ALOG_ASSERT(increment <= mFrameCountP2);
110 if ((index & mask) + increment >= mFrameCount) {
111 increment += mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800112 }
113 index += increment;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700114 ALOG_ASSERT((index & mask) < mFrameCount);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800115 return index;
116 } else {
117 return index + increment;
118 }
119}
120
Glenn Kasten09acf782016-06-17 10:28:05 -0700121int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700122 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800123{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700124 uint32_t diff = rear - front;
125 if (mFudgeFactor) {
126 uint32_t mask = mFrameCountP2 - 1;
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700127 uint32_t rearOffset = rear & mask;
128 uint32_t frontOffset = front & mask;
129 if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700130 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700131 }
132 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800133 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700134 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700135 if (lost != NULL) {
136 // TODO provide a more accurate estimate
137 *lost = (genDiff / mFrameCountP2) * mFrameCount;
138 }
139 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700140 }
141 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800142 }
143 }
144 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700145 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700146 if (lost != NULL) {
147 *lost = diff - mFrameCount;
148 }
149 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700150 }
151 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800152}
153
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700154////////////////////////////////////////////////////////////////////////////////
155
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700156audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700157 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700158 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700159 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700160 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700161{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700162 // 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 -0700163 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700164 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700165 frameCount > ((uint32_t) INT32_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700166}
167
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700168audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
169 bool throttlesWriter) :
170 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
171 throttlesWriter ? &mSingleProcessSharedFront : NULL)
172{
173}
174
Glenn Kasten09acf782016-06-17 10:28:05 -0700175audio_utils_fifo::~audio_utils_fifo()
176{
177}
178
179////////////////////////////////////////////////////////////////////////////////
180
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700181audio_utils_fifo_provider::audio_utils_fifo_provider() :
182 mObtained(0)
183{
184}
185
186audio_utils_fifo_provider::~audio_utils_fifo_provider()
187{
188}
189
190////////////////////////////////////////////////////////////////////////////////
191
192audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700193 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700194 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
195 mArmed(true), // because initial fill level of zero is < mLowLevelArm
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700196 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700197{
198}
199
200audio_utils_fifo_writer::~audio_utils_fifo_writer()
201{
202}
203
Glenn Kastend9942f72016-10-16 14:51:15 -0700204ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
205 const struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700206 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800207{
Glenn Kasten547a9922016-06-15 13:07:31 -0700208 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700209 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700210 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700211 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
212 iovec[0].mLength * mFifo.mFrameSize);
213 if (iovec[1].mLength > 0) {
214 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
215 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
216 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700217 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700218 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700219 }
220 return availToWrite;
221}
222
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700223// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700224ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700225 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700226 __attribute__((no_sanitize("integer")))
227{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700228 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700229 size_t availToWrite;
230 if (mFifo.mThrottleFront != NULL) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700231 uint32_t front;
232 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700233 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
234 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700235 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700236 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700237 err = filled;
238 availToWrite = 0;
239 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700240 }
241 availToWrite = mEffectiveFrames > (uint32_t) filled ?
242 mEffectiveFrames - (uint32_t) filled : 0;
243 // TODO pull out "count == 0"
244 if (count == 0 || availToWrite > 0 || timeout == NULL ||
245 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
246 break;
247 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700248 // TODO add comments
249 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700250 // the high level code (synchronization, sleep, futex, iovec) should be completely
251 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700252 int op = FUTEX_WAIT;
253 switch (mFifo.mThrottleFrontSync) {
254 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
255 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
256 if (err < 0) {
257 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
258 err = -errno;
259 } else {
260 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700261 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700262 break;
263 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
264 op = FUTEX_WAIT_PRIVATE;
265 // fall through
266 case AUDIO_UTILS_FIFO_SYNC_SHARED:
267 if (timeout->tv_sec == LONG_MAX) {
268 timeout = NULL;
269 }
270 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
271 if (err < 0) {
272 switch (errno) {
273 case EINTR:
274 case ETIMEDOUT:
275 err = -errno;
276 break;
277 default:
278 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
279 break;
280 }
281 }
282 break;
283 default:
284 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
285 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700286 }
287 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700288 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700289 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700290 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700291 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800292 if (availToWrite > count) {
293 availToWrite = count;
294 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700295 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
296 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800297 if (part1 > availToWrite) {
298 part1 = availToWrite;
299 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700300 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700301 // return slice
302 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700303 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700304 iovec[0].mLength = part1;
305 iovec[1].mOffset = 0;
306 iovec[1].mLength = part2;
307 mObtained = availToWrite;
308 }
309 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800310}
311
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700312void audio_utils_fifo_writer::release(size_t count)
313 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700314{
315 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700316 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700317 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700318 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700319 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700320 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700321 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700322 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700323 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700324 // TODO add comments
325 int op = FUTEX_WAKE;
326 switch (mFifo.mWriterRearSync) {
327 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
328 break;
329 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
330 op = FUTEX_WAKE_PRIVATE;
331 // fall through
332 case AUDIO_UTILS_FIFO_SYNC_SHARED:
333 if (filled >= 0) {
334 if ((uint32_t) filled < mLowLevelArm) {
335 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700336 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700337 if (mArmed && filled + count > mHighLevelTrigger) {
338 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700339 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700340 // err is number of processes woken up
341 if (err < 0) {
342 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
343 __func__, err, errno);
344 }
345 mArmed = false;
346 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700347 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700348 break;
349 default:
350 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
351 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700352 }
353 } else {
354 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700355 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700356 std::memory_order_release);
357 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700358 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700359 }
360}
361
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700362ssize_t audio_utils_fifo_writer::available()
363{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700364 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700365 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
366}
367
368void audio_utils_fifo_writer::resize(uint32_t frameCount)
369{
370 // cap to range [0, mFifo.mFrameCount]
371 if (frameCount > mFifo.mFrameCount) {
372 frameCount = mFifo.mFrameCount;
373 }
374 // if we reduce the effective frame count, update hysteresis points to be within the new range
375 if (frameCount < mEffectiveFrames) {
376 if (mLowLevelArm > frameCount) {
377 mLowLevelArm = frameCount;
378 }
379 if (mHighLevelTrigger > frameCount) {
380 mHighLevelTrigger = frameCount;
381 }
382 }
383 mEffectiveFrames = frameCount;
384}
385
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700386uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700387{
388 return mEffectiveFrames;
389}
390
391void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
392{
393 // cap to range [0, mEffectiveFrames]
394 if (lowLevelArm > mEffectiveFrames) {
395 lowLevelArm = mEffectiveFrames;
396 }
397 if (highLevelTrigger > mEffectiveFrames) {
398 highLevelTrigger = mEffectiveFrames;
399 }
400 // TODO this is overly conservative; it would be better to arm based on actual fill level
401 if (lowLevelArm > mLowLevelArm) {
402 mArmed = true;
403 }
404 mLowLevelArm = lowLevelArm;
405 mHighLevelTrigger = highLevelTrigger;
406}
407
408void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
409{
410 *lowLevelArm = mLowLevelArm;
411 *highLevelTrigger = mHighLevelTrigger;
412}
413
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700414////////////////////////////////////////////////////////////////////////////////
415
416audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700417 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
418 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700419 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
420 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700421{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700422}
423
424audio_utils_fifo_reader::~audio_utils_fifo_reader()
425{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700426 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700427}
428
Glenn Kastend9942f72016-10-16 14:51:15 -0700429ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700430 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700431 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800432{
Glenn Kasten547a9922016-06-15 13:07:31 -0700433 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700434 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700435 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700436 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
437 iovec[0].mLength * mFifo.mFrameSize);
438 if (iovec[1].mLength > 0) {
439 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
440 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
441 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700442 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700443 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700444 }
445 return availToRead;
446}
447
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700448ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700449 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700450 __attribute__((no_sanitize("integer")))
451{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700452 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700453}
454
455void audio_utils_fifo_reader::release(size_t count)
456 __attribute__((no_sanitize("integer")))
457{
458 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700459 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700460 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700461 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700462 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700463 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700464 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700465 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700466 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700467 // TODO add comments
468 int op = FUTEX_WAKE;
469 switch (mFifo.mThrottleFrontSync) {
470 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
471 break;
472 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
473 op = FUTEX_WAKE_PRIVATE;
474 // fall through
475 case AUDIO_UTILS_FIFO_SYNC_SHARED:
476 if (filled >= 0) {
477 if (filled > mHighLevelArm) {
478 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700479 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700480 if (mArmed && filled - count < mLowLevelTrigger) {
481 int err = sys_futex(&mThrottleFront->mIndex,
482 op, 1 /*waiters*/, NULL, NULL, 0);
483 // err is number of processes woken up
484 if (err < 0 || err > 1) {
485 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
486 __func__, err, errno);
487 }
488 mArmed = false;
489 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700490 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700491 break;
492 default:
493 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
494 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700495 }
496 } else {
497 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700498 }
499 mObtained -= count;
500 }
501}
502
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700503// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700504ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700505 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700506 __attribute__((no_sanitize("integer")))
507{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700508 int err = 0;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700509 uint32_t rear;
510 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700511 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700512 std::memory_order_acquire);
513 // TODO pull out "count == 0"
514 if (count == 0 || rear != mLocalFront || timeout == NULL ||
515 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
516 break;
517 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700518 // TODO add comments
519 int op = FUTEX_WAIT;
520 switch (mFifo.mWriterRearSync) {
521 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
522 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
523 if (err < 0) {
524 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
525 err = -errno;
526 } else {
527 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700528 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700529 break;
530 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
531 op = FUTEX_WAIT_PRIVATE;
532 // fall through
533 case AUDIO_UTILS_FIFO_SYNC_SHARED:
534 if (timeout->tv_sec == LONG_MAX) {
535 timeout = NULL;
536 }
537 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
538 if (err < 0) {
539 switch (errno) {
540 case EINTR:
541 case ETIMEDOUT:
542 err = -errno;
543 break;
544 default:
545 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
546 break;
547 }
548 }
549 break;
550 default:
551 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
552 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700553 }
554 timeout = NULL;
555 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700556 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700557 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700558 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700559 mLocalFront = rear;
560 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700561 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700562 err = filled;
563 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700564 }
565 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800566 if (availToRead > count) {
567 availToRead = count;
568 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700569 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
570 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800571 if (part1 > availToRead) {
572 part1 = availToRead;
573 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700574 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700575 // return slice
576 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700577 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700578 iovec[0].mLength = part1;
579 iovec[1].mOffset = 0;
580 iovec[1].mLength = part2;
581 mObtained = availToRead;
582 }
583 return availToRead > 0 ? availToRead : err;
584}
585
586ssize_t audio_utils_fifo_reader::available()
587{
588 return available(NULL /*lost*/);
589}
590
591ssize_t audio_utils_fifo_reader::available(size_t *lost)
592{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700593 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700594 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
595}
596
597void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
598{
599 // cap to range [0, mFifo.mFrameCount]
600 if (highLevelArm < 0) {
601 highLevelArm = -1;
602 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
603 highLevelArm = mFifo.mFrameCount;
604 }
605 if (lowLevelTrigger > mFifo.mFrameCount) {
606 lowLevelTrigger = mFifo.mFrameCount;
607 }
608 // TODO this is overly conservative; it would be better to arm based on actual fill level
609 if (highLevelArm < mHighLevelArm) {
610 mArmed = true;
611 }
612 mHighLevelArm = highLevelArm;
613 mLowLevelTrigger = lowLevelTrigger;
614}
615
616void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
617{
618 *highLevelArm = mHighLevelArm;
619 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700620}