blob: 9cdd48fd2f94be0f4a2db450e4345e81f261cb01 [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 Kasten44001d42016-10-19 15:46:02 -0700124 // TODO replace multiple returns by a single return point so this isn't needed
125 if (lost != NULL) {
126 *lost = 0;
127 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700128 uint32_t diff = rear - front;
129 if (mFudgeFactor) {
130 uint32_t mask = mFrameCountP2 - 1;
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700131 uint32_t rearOffset = rear & mask;
132 uint32_t frontOffset = front & mask;
133 if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700134 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700135 }
136 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800137 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700138 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700139 if (lost != NULL) {
140 // TODO provide a more accurate estimate
141 *lost = (genDiff / mFrameCountP2) * mFrameCount;
142 }
143 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700144 }
145 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800146 }
147 }
148 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700149 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700150 if (lost != NULL) {
Glenn Kasten44001d42016-10-19 15:46:02 -0700151 *lost = diff;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700152 }
153 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700154 }
155 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800156}
157
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700158////////////////////////////////////////////////////////////////////////////////
159
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700160audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700161 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700162 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700163 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700164 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700165{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700166 // 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 -0700167 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700168 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700169 frameCount > ((uint32_t) INT32_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700170}
171
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700172audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
173 bool throttlesWriter) :
174 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
175 throttlesWriter ? &mSingleProcessSharedFront : NULL)
176{
177}
178
Glenn Kasten09acf782016-06-17 10:28:05 -0700179audio_utils_fifo::~audio_utils_fifo()
180{
181}
182
183////////////////////////////////////////////////////////////////////////////////
184
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700185audio_utils_fifo_provider::audio_utils_fifo_provider() :
186 mObtained(0)
187{
188}
189
190audio_utils_fifo_provider::~audio_utils_fifo_provider()
191{
192}
193
194////////////////////////////////////////////////////////////////////////////////
195
196audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700197 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700198 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
199 mArmed(true), // because initial fill level of zero is < mLowLevelArm
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700200 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700201{
202}
203
204audio_utils_fifo_writer::~audio_utils_fifo_writer()
205{
206}
207
Glenn Kastend9942f72016-10-16 14:51:15 -0700208ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
209 const struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700210 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800211{
Glenn Kasten547a9922016-06-15 13:07:31 -0700212 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700213 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700214 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700215 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
216 iovec[0].mLength * mFifo.mFrameSize);
217 if (iovec[1].mLength > 0) {
218 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
219 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
220 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700221 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700222 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700223 }
224 return availToWrite;
225}
226
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700227// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700228ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700229 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700230 __attribute__((no_sanitize("integer")))
231{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700232 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700233 size_t availToWrite;
234 if (mFifo.mThrottleFront != NULL) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700235 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700236 uint32_t front;
237 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700238 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
239 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700240 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700241 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700242 err = filled;
243 availToWrite = 0;
244 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700245 }
246 availToWrite = mEffectiveFrames > (uint32_t) filled ?
247 mEffectiveFrames - (uint32_t) filled : 0;
248 // TODO pull out "count == 0"
249 if (count == 0 || availToWrite > 0 || timeout == NULL ||
250 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
251 break;
252 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700253 // TODO add comments
254 // TODO abstract out switch and replace by general sync object
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700255 // the high level code (synchronization, sleep, futex, iovec) should be completely
256 // separate from the low level code (indexes, available, masking).
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700257 int op = FUTEX_WAIT;
258 switch (mFifo.mThrottleFrontSync) {
259 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
260 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
261 if (err < 0) {
262 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
263 err = -errno;
264 } else {
265 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700266 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700267 break;
268 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
269 op = FUTEX_WAIT_PRIVATE;
270 // fall through
271 case AUDIO_UTILS_FIFO_SYNC_SHARED:
272 if (timeout->tv_sec == LONG_MAX) {
273 timeout = NULL;
274 }
275 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
276 if (err < 0) {
277 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700278 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700279 // Benign race condition with partner: mFifo.mThrottleFront->mIndex
280 // changed value between the earlier atomic_load_explicit() and sys_futex().
281 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700282 if (retries-- > 0) {
283 // bypass the "timeout = NULL;" below
284 continue;
285 }
286 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700287 case EINTR:
288 case ETIMEDOUT:
289 err = -errno;
290 break;
291 default:
292 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
293 break;
294 }
295 }
296 break;
297 default:
298 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
299 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700300 }
301 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700302 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700303 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700304 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700305 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800306 if (availToWrite > count) {
307 availToWrite = count;
308 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700309 uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
310 size_t part1 = mFifo.mFrameCount - rearOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800311 if (part1 > availToWrite) {
312 part1 = availToWrite;
313 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700314 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700315 // return slice
316 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700317 iovec[0].mOffset = rearOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700318 iovec[0].mLength = part1;
319 iovec[1].mOffset = 0;
320 iovec[1].mLength = part2;
321 mObtained = availToWrite;
322 }
323 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800324}
325
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700326void audio_utils_fifo_writer::release(size_t count)
327 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700328{
329 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700330 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700331 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700332 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700333 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700334 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700335 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700336 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700337 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700338 // TODO add comments
339 int op = FUTEX_WAKE;
340 switch (mFifo.mWriterRearSync) {
341 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
342 break;
343 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
344 op = FUTEX_WAKE_PRIVATE;
345 // fall through
346 case AUDIO_UTILS_FIFO_SYNC_SHARED:
347 if (filled >= 0) {
348 if ((uint32_t) filled < mLowLevelArm) {
349 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700350 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700351 if (mArmed && filled + count > mHighLevelTrigger) {
352 int err = sys_futex(&mFifo.mWriterRear.mIndex,
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700353 op, INT32_MAX /*waiters*/, NULL, NULL, 0);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700354 // err is number of processes woken up
355 if (err < 0) {
356 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
357 __func__, err, errno);
358 }
359 mArmed = false;
360 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700361 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700362 break;
363 default:
364 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
365 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700366 }
367 } else {
368 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700369 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700370 std::memory_order_release);
371 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700372 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700373 }
374}
375
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700376ssize_t audio_utils_fifo_writer::available()
377{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700378 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700379 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
380}
381
382void audio_utils_fifo_writer::resize(uint32_t frameCount)
383{
384 // cap to range [0, mFifo.mFrameCount]
385 if (frameCount > mFifo.mFrameCount) {
386 frameCount = mFifo.mFrameCount;
387 }
388 // if we reduce the effective frame count, update hysteresis points to be within the new range
389 if (frameCount < mEffectiveFrames) {
390 if (mLowLevelArm > frameCount) {
391 mLowLevelArm = frameCount;
392 }
393 if (mHighLevelTrigger > frameCount) {
394 mHighLevelTrigger = frameCount;
395 }
396 }
397 mEffectiveFrames = frameCount;
398}
399
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700400uint32_t audio_utils_fifo_writer::size() const
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700401{
402 return mEffectiveFrames;
403}
404
405void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
406{
407 // cap to range [0, mEffectiveFrames]
408 if (lowLevelArm > mEffectiveFrames) {
409 lowLevelArm = mEffectiveFrames;
410 }
411 if (highLevelTrigger > mEffectiveFrames) {
412 highLevelTrigger = mEffectiveFrames;
413 }
414 // TODO this is overly conservative; it would be better to arm based on actual fill level
415 if (lowLevelArm > mLowLevelArm) {
416 mArmed = true;
417 }
418 mLowLevelArm = lowLevelArm;
419 mHighLevelTrigger = highLevelTrigger;
420}
421
422void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
423{
424 *lowLevelArm = mLowLevelArm;
425 *highLevelTrigger = mHighLevelTrigger;
426}
427
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700428////////////////////////////////////////////////////////////////////////////////
429
430audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700431 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
432 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700433 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
434 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700435{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700436}
437
438audio_utils_fifo_reader::~audio_utils_fifo_reader()
439{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700440 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700441}
442
Glenn Kastend9942f72016-10-16 14:51:15 -0700443ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700444 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700445 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800446{
Glenn Kasten547a9922016-06-15 13:07:31 -0700447 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700448 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700449 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700450 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
451 iovec[0].mLength * mFifo.mFrameSize);
452 if (iovec[1].mLength > 0) {
453 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
454 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
455 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700456 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700457 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700458 }
459 return availToRead;
460}
461
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700462ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700463 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700464 __attribute__((no_sanitize("integer")))
465{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700466 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700467}
468
469void audio_utils_fifo_reader::release(size_t count)
470 __attribute__((no_sanitize("integer")))
471{
472 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700473 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700474 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700475 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700476 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700477 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700478 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700479 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700480 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700481 // TODO add comments
482 int op = FUTEX_WAKE;
483 switch (mFifo.mThrottleFrontSync) {
484 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
485 break;
486 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
487 op = FUTEX_WAKE_PRIVATE;
488 // fall through
489 case AUDIO_UTILS_FIFO_SYNC_SHARED:
490 if (filled >= 0) {
491 if (filled > mHighLevelArm) {
492 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700493 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700494 if (mArmed && filled - count < mLowLevelTrigger) {
495 int err = sys_futex(&mThrottleFront->mIndex,
496 op, 1 /*waiters*/, NULL, NULL, 0);
497 // err is number of processes woken up
498 if (err < 0 || err > 1) {
499 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
500 __func__, err, errno);
501 }
502 mArmed = false;
503 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700504 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700505 break;
506 default:
507 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
508 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700509 }
510 } else {
511 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700512 }
513 mObtained -= count;
514 }
515}
516
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700517// iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700518ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700519 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700520 __attribute__((no_sanitize("integer")))
521{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700522 int err = 0;
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700523 int retries = kRetries;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700524 uint32_t rear;
525 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700526 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700527 std::memory_order_acquire);
528 // TODO pull out "count == 0"
529 if (count == 0 || rear != mLocalFront || timeout == NULL ||
530 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
531 break;
532 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700533 // TODO add comments
534 int op = FUTEX_WAIT;
535 switch (mFifo.mWriterRearSync) {
536 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
537 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
538 if (err < 0) {
539 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
540 err = -errno;
541 } else {
542 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700543 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700544 break;
545 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
546 op = FUTEX_WAIT_PRIVATE;
547 // fall through
548 case AUDIO_UTILS_FIFO_SYNC_SHARED:
549 if (timeout->tv_sec == LONG_MAX) {
550 timeout = NULL;
551 }
552 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
553 if (err < 0) {
554 switch (errno) {
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700555 case EWOULDBLOCK:
Glenn Kasten7cabff92016-10-19 11:13:55 -0700556 // Benign race condition with partner: mFifo.mWriterRear->mIndex
557 // changed value between the earlier atomic_load_explicit() and sys_futex().
558 // Try to load index again, but give up if we are unable to converge.
Glenn Kastenf277a7c2016-10-14 08:51:20 -0700559 if (retries-- > 0) {
560 // bypass the "timeout = NULL;" below
561 continue;
562 }
563 // fall through
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700564 case EINTR:
565 case ETIMEDOUT:
566 err = -errno;
567 break;
568 default:
569 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
570 break;
571 }
572 }
573 break;
574 default:
575 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
576 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700577 }
578 timeout = NULL;
579 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700580 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700581 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700582 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700583 mLocalFront = rear;
584 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700585 // on error, return an empty slice
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700586 err = filled;
587 filled = 0;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700588 }
589 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800590 if (availToRead > count) {
591 availToRead = count;
592 }
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700593 uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
594 size_t part1 = mFifo.mFrameCount - frontOffset;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800595 if (part1 > availToRead) {
596 part1 = availToRead;
597 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700598 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700599 // return slice
600 if (iovec != NULL) {
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700601 iovec[0].mOffset = frontOffset;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700602 iovec[0].mLength = part1;
603 iovec[1].mOffset = 0;
604 iovec[1].mLength = part2;
605 mObtained = availToRead;
606 }
607 return availToRead > 0 ? availToRead : err;
608}
609
610ssize_t audio_utils_fifo_reader::available()
611{
612 return available(NULL /*lost*/);
613}
614
615ssize_t audio_utils_fifo_reader::available(size_t *lost)
616{
Glenn Kasten9ddeb2c2016-10-14 08:49:02 -0700617 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700618 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
619}
620
621void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
622{
623 // cap to range [0, mFifo.mFrameCount]
624 if (highLevelArm < 0) {
625 highLevelArm = -1;
626 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
627 highLevelArm = mFifo.mFrameCount;
628 }
629 if (lowLevelTrigger > mFifo.mFrameCount) {
630 lowLevelTrigger = mFifo.mFrameCount;
631 }
632 // TODO this is overly conservative; it would be better to arm based on actual fill level
633 if (highLevelArm < mHighLevelArm) {
634 mArmed = true;
635 }
636 mHighLevelArm = highLevelArm;
637 mLowLevelTrigger = lowLevelTrigger;
638}
639
640void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
641{
642 *highLevelArm = mHighLevelArm;
643 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700644}