blob: cbd7e2fed656d8555cc5828bd4bee4a1bfc5fe34 [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 Kasten3ddacbb2016-06-20 14:41:03 -070068static int sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
69{
Glenn Kasten9052f3b2016-07-08 16:24:41 -070070#ifdef __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070071 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
Glenn Kastenc0924bc2016-10-02 13:00:19 -070072#else // __linux__
73 // macOS doesn't have futex
Glenn Kasten86c4a6d2016-07-09 10:37:17 -070074 (void) addr1;
75 (void) op;
76 (void) val1;
77 (void) timeout;
78 (void) addr2;
79 (void) val3;
Glenn Kasten9052f3b2016-07-08 16:24:41 -070080 errno = ENOSYS;
81 return -1;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070082#endif // __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070083}
84
Glenn Kastendc1ff1f2016-09-02 13:54:59 -070085audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
Glenn Kastenc0924bc2016-10-02 13:00:19 -070086 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten6d7ad762016-06-15 17:05:54 -070087 __attribute__((no_sanitize("integer"))) :
Glenn Kasten9b4fe472016-06-13 09:34:57 -070088 mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
Glenn Kasten09acf782016-06-17 10:28:05 -070089 mFudgeFactor(mFrameCountP2 - mFrameCount),
Glenn Kastenc0924bc2016-10-02 13:00:19 -070090 // FIXME need an API to configure the sync types
91 mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
92 mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED)
Glenn Kasten9b4c8052015-01-06 14:13:13 -080093{
Glenn Kasten09acf782016-06-17 10:28:05 -070094 // actual upper bound on frameCount will depend on the frame size
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -070095 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
Glenn Kasten9b4c8052015-01-06 14:13:13 -080096}
97
Glenn Kasten09acf782016-06-17 10:28:05 -070098audio_utils_fifo_base::~audio_utils_fifo_base()
Glenn Kasten9b4c8052015-01-06 14:13:13 -080099{
100}
101
Glenn Kasten09acf782016-06-17 10:28:05 -0700102uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700103 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800104{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700105 if (mFudgeFactor) {
106 uint32_t mask = mFrameCountP2 - 1;
107 ALOG_ASSERT((index & mask) < mFrameCount);
108 ALOG_ASSERT(increment <= mFrameCountP2);
109 if ((index & mask) + increment >= mFrameCount) {
110 increment += mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800111 }
112 index += increment;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700113 ALOG_ASSERT((index & mask) < mFrameCount);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800114 return index;
115 } else {
116 return index + increment;
117 }
118}
119
Glenn Kasten09acf782016-06-17 10:28:05 -0700120int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700121 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800122{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700123 uint32_t diff = rear - front;
124 if (mFudgeFactor) {
125 uint32_t mask = mFrameCountP2 - 1;
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700126 uint32_t rearOffset = rear & mask;
127 uint32_t frontOffset = front & mask;
128 if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700129 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700130 }
131 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800132 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700133 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700134 if (lost != NULL) {
135 // TODO provide a more accurate estimate
136 *lost = (genDiff / mFrameCountP2) * mFrameCount;
137 }
138 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700139 }
140 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800141 }
142 }
143 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700144 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700145 if (lost != NULL) {
146 *lost = diff - mFrameCount;
147 }
148 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700149 }
150 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800151}
152
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700153////////////////////////////////////////////////////////////////////////////////
154
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700155audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700156 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700157 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700158 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700159 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700160{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700161 // 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 -0700162 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700163 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700164 frameCount > ((uint32_t) INT32_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700165}
166
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700167audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
168 bool throttlesWriter) :
169 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
170 throttlesWriter ? &mSingleProcessSharedFront : NULL)
171{
172}
173
Glenn Kasten09acf782016-06-17 10:28:05 -0700174audio_utils_fifo::~audio_utils_fifo()
175{
176}
177
178////////////////////////////////////////////////////////////////////////////////
179
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700180audio_utils_fifo_provider::audio_utils_fifo_provider() :
181 mObtained(0)
182{
183}
184
185audio_utils_fifo_provider::~audio_utils_fifo_provider()
186{
187}
188
189////////////////////////////////////////////////////////////////////////////////
190
191audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700192 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700193 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
194 mArmed(true), // because initial fill level of zero is < mLowLevelArm
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700195 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700196{
197}
198
199audio_utils_fifo_writer::~audio_utils_fifo_writer()
200{
201}
202
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700203ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count, struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700204 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800205{
Glenn Kasten547a9922016-06-15 13:07:31 -0700206 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700207 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700208 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700209 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
210 iovec[0].mLength * mFifo.mFrameSize);
211 if (iovec[1].mLength > 0) {
212 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
213 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
214 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700215 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700216 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700217 }
218 return availToWrite;
219}
220
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700221// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700222ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
223 struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700224 __attribute__((no_sanitize("integer")))
225{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700226 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700227 size_t availToWrite;
228 if (mFifo.mThrottleFront != NULL) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700229 uint32_t front;
230 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700231 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
232 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700233 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700234 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700235 err = filled;
236 availToWrite = 0;
237 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700238 }
239 availToWrite = mEffectiveFrames > (uint32_t) filled ?
240 mEffectiveFrames - (uint32_t) filled : 0;
241 // TODO pull out "count == 0"
242 if (count == 0 || availToWrite > 0 || timeout == NULL ||
243 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
244 break;
245 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700246 // TODO add comments
247 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700248 // the high level code (synchronization, sleep, futex, iovec) should be completely
249 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700250 int op = FUTEX_WAIT;
251 switch (mFifo.mThrottleFrontSync) {
252 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
253 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
254 if (err < 0) {
255 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
256 err = -errno;
257 } else {
258 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700259 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700260 break;
261 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
262 op = FUTEX_WAIT_PRIVATE;
263 // fall through
264 case AUDIO_UTILS_FIFO_SYNC_SHARED:
265 if (timeout->tv_sec == LONG_MAX) {
266 timeout = NULL;
267 }
268 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
269 if (err < 0) {
270 switch (errno) {
271 case EINTR:
272 case ETIMEDOUT:
273 err = -errno;
274 break;
275 default:
276 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
277 break;
278 }
279 }
280 break;
281 default:
282 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
283 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700284 }
285 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700286 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700287 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700288 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700289 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800290 if (availToWrite > count) {
291 availToWrite = count;
292 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700293 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
294 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800295 if (part1 > availToWrite) {
296 part1 = availToWrite;
297 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700298 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700299 // return slice
300 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700301 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700302 iovec[0].mLength = part1;
303 iovec[1].mOffset = 0;
304 iovec[1].mLength = part2;
305 mObtained = availToWrite;
306 }
307 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800308}
309
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700310void audio_utils_fifo_writer::release(size_t count)
311 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700312{
313 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700314 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700315 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700316 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700317 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700318 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700319 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700320 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700321 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700322 // TODO add comments
323 int op = FUTEX_WAKE;
324 switch (mFifo.mWriterRearSync) {
325 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
326 break;
327 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
328 op = FUTEX_WAKE_PRIVATE;
329 // fall through
330 case AUDIO_UTILS_FIFO_SYNC_SHARED:
331 if (filled >= 0) {
332 if ((uint32_t) filled < mLowLevelArm) {
333 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700334 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700335 if (mArmed && filled + count > mHighLevelTrigger) {
336 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700337 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700338 // err is number of processes woken up
339 if (err < 0) {
340 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
341 __func__, err, errno);
342 }
343 mArmed = false;
344 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700345 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700346 break;
347 default:
348 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
349 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700350 }
351 } else {
352 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700353 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700354 std::memory_order_release);
355 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700356 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700357 }
358}
359
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700360ssize_t audio_utils_fifo_writer::available()
361{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700362 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700363 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
364}
365
366void audio_utils_fifo_writer::resize(uint32_t frameCount)
367{
368 // cap to range [0, mFifo.mFrameCount]
369 if (frameCount > mFifo.mFrameCount) {
370 frameCount = mFifo.mFrameCount;
371 }
372 // if we reduce the effective frame count, update hysteresis points to be within the new range
373 if (frameCount < mEffectiveFrames) {
374 if (mLowLevelArm > frameCount) {
375 mLowLevelArm = frameCount;
376 }
377 if (mHighLevelTrigger > frameCount) {
378 mHighLevelTrigger = frameCount;
379 }
380 }
381 mEffectiveFrames = frameCount;
382}
383
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700384uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700385{
386 return mEffectiveFrames;
387}
388
389void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
390{
391 // cap to range [0, mEffectiveFrames]
392 if (lowLevelArm > mEffectiveFrames) {
393 lowLevelArm = mEffectiveFrames;
394 }
395 if (highLevelTrigger > mEffectiveFrames) {
396 highLevelTrigger = mEffectiveFrames;
397 }
398 // TODO this is overly conservative; it would be better to arm based on actual fill level
399 if (lowLevelArm > mLowLevelArm) {
400 mArmed = true;
401 }
402 mLowLevelArm = lowLevelArm;
403 mHighLevelTrigger = highLevelTrigger;
404}
405
406void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
407{
408 *lowLevelArm = mLowLevelArm;
409 *highLevelTrigger = mHighLevelTrigger;
410}
411
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700412////////////////////////////////////////////////////////////////////////////////
413
414audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700415 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
416 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700417 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
418 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700419{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700420}
421
422audio_utils_fifo_reader::~audio_utils_fifo_reader()
423{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700424 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700425}
426
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700427ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, struct timespec *timeout,
428 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700429 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800430{
Glenn Kasten547a9922016-06-15 13:07:31 -0700431 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700432 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700433 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700434 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
435 iovec[0].mLength * mFifo.mFrameSize);
436 if (iovec[1].mLength > 0) {
437 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
438 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
439 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700440 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700441 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700442 }
443 return availToRead;
444}
445
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700446ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
447 struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700448 __attribute__((no_sanitize("integer")))
449{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700450 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700451}
452
453void audio_utils_fifo_reader::release(size_t count)
454 __attribute__((no_sanitize("integer")))
455{
456 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700457 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700458 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700459 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700460 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700461 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700462 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700463 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700464 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700465 // TODO add comments
466 int op = FUTEX_WAKE;
467 switch (mFifo.mThrottleFrontSync) {
468 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
469 break;
470 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
471 op = FUTEX_WAKE_PRIVATE;
472 // fall through
473 case AUDIO_UTILS_FIFO_SYNC_SHARED:
474 if (filled >= 0) {
475 if (filled > mHighLevelArm) {
476 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700477 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700478 if (mArmed && filled - count < mLowLevelTrigger) {
479 int err = sys_futex(&mThrottleFront->mIndex,
480 op, 1 /*waiters*/, NULL, NULL, 0);
481 // err is number of processes woken up
482 if (err < 0 || err > 1) {
483 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
484 __func__, err, errno);
485 }
486 mArmed = false;
487 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700488 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700489 break;
490 default:
491 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
492 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700493 }
494 } else {
495 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700496 }
497 mObtained -= count;
498 }
499}
500
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700501// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700502ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
503 struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700504 __attribute__((no_sanitize("integer")))
505{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700506 int err = 0;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700507 uint32_t rear;
508 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700509 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700510 std::memory_order_acquire);
511 // TODO pull out "count == 0"
512 if (count == 0 || rear != mLocalFront || timeout == NULL ||
513 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
514 break;
515 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700516 // TODO add comments
517 int op = FUTEX_WAIT;
518 switch (mFifo.mWriterRearSync) {
519 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
520 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
521 if (err < 0) {
522 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
523 err = -errno;
524 } else {
525 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700526 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700527 break;
528 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
529 op = FUTEX_WAIT_PRIVATE;
530 // fall through
531 case AUDIO_UTILS_FIFO_SYNC_SHARED:
532 if (timeout->tv_sec == LONG_MAX) {
533 timeout = NULL;
534 }
535 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
536 if (err < 0) {
537 switch (errno) {
538 case EINTR:
539 case ETIMEDOUT:
540 err = -errno;
541 break;
542 default:
543 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
544 break;
545 }
546 }
547 break;
548 default:
549 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
550 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700551 }
552 timeout = NULL;
553 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700554 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700555 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700556 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700557 mLocalFront = rear;
558 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700559 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700560 err = filled;
561 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700562 }
563 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800564 if (availToRead > count) {
565 availToRead = count;
566 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700567 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
568 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800569 if (part1 > availToRead) {
570 part1 = availToRead;
571 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700572 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700573 // return slice
574 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700575 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700576 iovec[0].mLength = part1;
577 iovec[1].mOffset = 0;
578 iovec[1].mLength = part2;
579 mObtained = availToRead;
580 }
581 return availToRead > 0 ? availToRead : err;
582}
583
584ssize_t audio_utils_fifo_reader::available()
585{
586 return available(NULL /*lost*/);
587}
588
589ssize_t audio_utils_fifo_reader::available(size_t *lost)
590{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700591 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700592 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
593}
594
595void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
596{
597 // cap to range [0, mFifo.mFrameCount]
598 if (highLevelArm < 0) {
599 highLevelArm = -1;
600 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
601 highLevelArm = mFifo.mFrameCount;
602 }
603 if (lowLevelTrigger > mFifo.mFrameCount) {
604 lowLevelTrigger = mFifo.mFrameCount;
605 }
606 // TODO this is overly conservative; it would be better to arm based on actual fill level
607 if (highLevelArm < mHighLevelArm) {
608 mArmed = true;
609 }
610 mHighLevelArm = highLevelArm;
611 mLowLevelTrigger = lowLevelTrigger;
612}
613
614void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
615{
616 *highLevelArm = mHighLevelArm;
617 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700618}