blob: b4cb401d2f0503c189d99aaaa8df15afb9bf21c5 [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 Kastenf277a7c2016-10-14 08:51:20 -0700231 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700232 uint32_t front;
233 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700234 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
235 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700236 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700237 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700238 err = filled;
239 availToWrite = 0;
240 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700241 }
242 availToWrite = mEffectiveFrames > (uint32_t) filled ?
243 mEffectiveFrames - (uint32_t) filled : 0;
244 // TODO pull out "count == 0"
245 if (count == 0 || availToWrite > 0 || timeout == NULL ||
246 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
247 break;
248 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700249 // TODO add comments
250 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700251 // the high level code (synchronization, sleep, futex, iovec) should be completely
252 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700253 int op = FUTEX_WAIT;
254 switch (mFifo.mThrottleFrontSync) {
255 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
256 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
257 if (err < 0) {
258 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
259 err = -errno;
260 } else {
261 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700262 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700263 break;
264 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
265 op = FUTEX_WAIT_PRIVATE;
266 // fall through
267 case AUDIO_UTILS_FIFO_SYNC_SHARED:
268 if (timeout->tv_sec == LONG_MAX) {
269 timeout = NULL;
270 }
271 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
272 if (err < 0) {
273 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700274 case EWOULDBLOCK:
275 // benign race condition with partner, try again
276 if (retries-- > 0) {
277 // bypass the "timeout = NULL;" below
278 continue;
279 }
280 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700281 case EINTR:
282 case ETIMEDOUT:
283 err = -errno;
284 break;
285 default:
286 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
287 break;
288 }
289 }
290 break;
291 default:
292 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
293 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700294 }
295 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700296 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700297 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700298 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700299 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800300 if (availToWrite > count) {
301 availToWrite = count;
302 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700303 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
304 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800305 if (part1 > availToWrite) {
306 part1 = availToWrite;
307 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700308 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700309 // return slice
310 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700311 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700312 iovec[0].mLength = part1;
313 iovec[1].mOffset = 0;
314 iovec[1].mLength = part2;
315 mObtained = availToWrite;
316 }
317 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800318}
319
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700320void audio_utils_fifo_writer::release(size_t count)
321 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700322{
323 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700324 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700325 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700326 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700327 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700328 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700329 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700330 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700331 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700332 // TODO add comments
333 int op = FUTEX_WAKE;
334 switch (mFifo.mWriterRearSync) {
335 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
336 break;
337 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
338 op = FUTEX_WAKE_PRIVATE;
339 // fall through
340 case AUDIO_UTILS_FIFO_SYNC_SHARED:
341 if (filled >= 0) {
342 if ((uint32_t) filled < mLowLevelArm) {
343 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700344 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700345 if (mArmed && filled + count > mHighLevelTrigger) {
346 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700347 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700348 // err is number of processes woken up
349 if (err < 0) {
350 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
351 __func__, err, errno);
352 }
353 mArmed = false;
354 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700355 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700356 break;
357 default:
358 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
359 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700360 }
361 } else {
362 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700363 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700364 std::memory_order_release);
365 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700366 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700367 }
368}
369
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700370ssize_t audio_utils_fifo_writer::available()
371{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700372 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700373 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
374}
375
376void audio_utils_fifo_writer::resize(uint32_t frameCount)
377{
378 // cap to range [0, mFifo.mFrameCount]
379 if (frameCount > mFifo.mFrameCount) {
380 frameCount = mFifo.mFrameCount;
381 }
382 // if we reduce the effective frame count, update hysteresis points to be within the new range
383 if (frameCount < mEffectiveFrames) {
384 if (mLowLevelArm > frameCount) {
385 mLowLevelArm = frameCount;
386 }
387 if (mHighLevelTrigger > frameCount) {
388 mHighLevelTrigger = frameCount;
389 }
390 }
391 mEffectiveFrames = frameCount;
392}
393
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700394uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700395{
396 return mEffectiveFrames;
397}
398
399void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
400{
401 // cap to range [0, mEffectiveFrames]
402 if (lowLevelArm > mEffectiveFrames) {
403 lowLevelArm = mEffectiveFrames;
404 }
405 if (highLevelTrigger > mEffectiveFrames) {
406 highLevelTrigger = mEffectiveFrames;
407 }
408 // TODO this is overly conservative; it would be better to arm based on actual fill level
409 if (lowLevelArm > mLowLevelArm) {
410 mArmed = true;
411 }
412 mLowLevelArm = lowLevelArm;
413 mHighLevelTrigger = highLevelTrigger;
414}
415
416void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
417{
418 *lowLevelArm = mLowLevelArm;
419 *highLevelTrigger = mHighLevelTrigger;
420}
421
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700422////////////////////////////////////////////////////////////////////////////////
423
424audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700425 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
426 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700427 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
428 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700429{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700430}
431
432audio_utils_fifo_reader::~audio_utils_fifo_reader()
433{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700434 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700435}
436
Glenn Kastend9942f72016-10-16 14:51:15 -0700437ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700438 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700439 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800440{
Glenn Kasten547a9922016-06-15 13:07:31 -0700441 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700442 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700443 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700444 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
445 iovec[0].mLength * mFifo.mFrameSize);
446 if (iovec[1].mLength > 0) {
447 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
448 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
449 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700450 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700451 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700452 }
453 return availToRead;
454}
455
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700456ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700457 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700458 __attribute__((no_sanitize("integer")))
459{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700460 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700461}
462
463void audio_utils_fifo_reader::release(size_t count)
464 __attribute__((no_sanitize("integer")))
465{
466 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700467 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700468 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700469 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700470 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700471 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700472 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700473 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700474 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700475 // TODO add comments
476 int op = FUTEX_WAKE;
477 switch (mFifo.mThrottleFrontSync) {
478 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
479 break;
480 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
481 op = FUTEX_WAKE_PRIVATE;
482 // fall through
483 case AUDIO_UTILS_FIFO_SYNC_SHARED:
484 if (filled >= 0) {
485 if (filled > mHighLevelArm) {
486 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700487 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700488 if (mArmed && filled - count < mLowLevelTrigger) {
489 int err = sys_futex(&mThrottleFront->mIndex,
490 op, 1 /*waiters*/, NULL, NULL, 0);
491 // err is number of processes woken up
492 if (err < 0 || err > 1) {
493 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
494 __func__, err, errno);
495 }
496 mArmed = false;
497 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700498 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700499 break;
500 default:
501 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
502 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700503 }
504 } else {
505 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700506 }
507 mObtained -= count;
508 }
509}
510
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700511// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700512ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700513 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700514 __attribute__((no_sanitize("integer")))
515{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700516 int err = 0;
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700517 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700518 uint32_t rear;
519 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700520 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700521 std::memory_order_acquire);
522 // TODO pull out "count == 0"
523 if (count == 0 || rear != mLocalFront || timeout == NULL ||
524 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
525 break;
526 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700527 // TODO add comments
528 int op = FUTEX_WAIT;
529 switch (mFifo.mWriterRearSync) {
530 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
531 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
532 if (err < 0) {
533 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
534 err = -errno;
535 } else {
536 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700537 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700538 break;
539 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
540 op = FUTEX_WAIT_PRIVATE;
541 // fall through
542 case AUDIO_UTILS_FIFO_SYNC_SHARED:
543 if (timeout->tv_sec == LONG_MAX) {
544 timeout = NULL;
545 }
546 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
547 if (err < 0) {
548 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700549 case EWOULDBLOCK:
550 // benign race condition with partner, try again
551 if (retries-- > 0) {
552 // bypass the "timeout = NULL;" below
553 continue;
554 }
555 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700556 case EINTR:
557 case ETIMEDOUT:
558 err = -errno;
559 break;
560 default:
561 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
562 break;
563 }
564 }
565 break;
566 default:
567 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
568 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700569 }
570 timeout = NULL;
571 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700572 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700573 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700574 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700575 mLocalFront = rear;
576 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700577 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700578 err = filled;
579 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700580 }
581 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800582 if (availToRead > count) {
583 availToRead = count;
584 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700585 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
586 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800587 if (part1 > availToRead) {
588 part1 = availToRead;
589 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700590 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700591 // return slice
592 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700593 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700594 iovec[0].mLength = part1;
595 iovec[1].mOffset = 0;
596 iovec[1].mLength = part2;
597 mObtained = availToRead;
598 }
599 return availToRead > 0 ? availToRead : err;
600}
601
602ssize_t audio_utils_fifo_reader::available()
603{
604 return available(NULL /*lost*/);
605}
606
607ssize_t audio_utils_fifo_reader::available(size_t *lost)
608{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700609 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700610 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
611}
612
613void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
614{
615 // cap to range [0, mFifo.mFrameCount]
616 if (highLevelArm < 0) {
617 highLevelArm = -1;
618 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
619 highLevelArm = mFifo.mFrameCount;
620 }
621 if (lowLevelTrigger > mFifo.mFrameCount) {
622 lowLevelTrigger = mFifo.mFrameCount;
623 }
624 // TODO this is overly conservative; it would be better to arm based on actual fill level
625 if (highLevelArm < mHighLevelArm) {
626 mArmed = true;
627 }
628 mHighLevelArm = highLevelArm;
629 mLowLevelTrigger = lowLevelTrigger;
630}
631
632void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
633{
634 *highLevelArm = mHighLevelArm;
635 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700636}