blob: 31a0c62c9bdd14b600f9047a4cc7c5523203fe2a [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
54int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request,
55 struct timespec *remain)
56{
57 errno = ENOSYS;
58 return -1;
59}
60#endif // __linux__
61
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070062static int sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
63{
Glenn Kasten9052f3b2016-07-08 16:24:41 -070064#ifdef __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070065 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
Glenn Kastenc0924bc2016-10-02 13:00:19 -070066#else // __linux__
67 // macOS doesn't have futex
Glenn Kasten86c4a6d2016-07-09 10:37:17 -070068 (void) addr1;
69 (void) op;
70 (void) val1;
71 (void) timeout;
72 (void) addr2;
73 (void) val3;
Glenn Kasten9052f3b2016-07-08 16:24:41 -070074 errno = ENOSYS;
75 return -1;
Glenn Kastenc0924bc2016-10-02 13:00:19 -070076#endif // __linux__
Glenn Kasten3ddacbb2016-06-20 14:41:03 -070077}
78
Glenn Kastendc1ff1f2016-09-02 13:54:59 -070079audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
Glenn Kastenc0924bc2016-10-02 13:00:19 -070080 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten6d7ad762016-06-15 17:05:54 -070081 __attribute__((no_sanitize("integer"))) :
Glenn Kasten9b4fe472016-06-13 09:34:57 -070082 mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
Glenn Kasten09acf782016-06-17 10:28:05 -070083 mFudgeFactor(mFrameCountP2 - mFrameCount),
Glenn Kastenc0924bc2016-10-02 13:00:19 -070084 // FIXME need an API to configure the sync types
85 mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
86 mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED)
Glenn Kasten9b4c8052015-01-06 14:13:13 -080087{
Glenn Kasten09acf782016-06-17 10:28:05 -070088 // actual upper bound on frameCount will depend on the frame size
Glenn Kasten0ab1d862016-06-20 12:04:56 -070089 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT_MAX));
Glenn Kasten9b4c8052015-01-06 14:13:13 -080090}
91
Glenn Kasten09acf782016-06-17 10:28:05 -070092audio_utils_fifo_base::~audio_utils_fifo_base()
Glenn Kasten9b4c8052015-01-06 14:13:13 -080093{
94}
95
Glenn Kasten09acf782016-06-17 10:28:05 -070096uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment)
Glenn Kasten9b4fe472016-06-13 09:34:57 -070097 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -080098{
Glenn Kasten9b4fe472016-06-13 09:34:57 -070099 if (mFudgeFactor) {
100 uint32_t mask = mFrameCountP2 - 1;
101 ALOG_ASSERT((index & mask) < mFrameCount);
102 ALOG_ASSERT(increment <= mFrameCountP2);
103 if ((index & mask) + increment >= mFrameCount) {
104 increment += mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800105 }
106 index += increment;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700107 ALOG_ASSERT((index & mask) < mFrameCount);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800108 return index;
109 } else {
110 return index + increment;
111 }
112}
113
Glenn Kasten09acf782016-06-17 10:28:05 -0700114int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700115 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800116{
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700117 uint32_t diff = rear - front;
118 if (mFudgeFactor) {
119 uint32_t mask = mFrameCountP2 - 1;
120 uint32_t rearMasked = rear & mask;
121 uint32_t frontMasked = front & mask;
122 if (rearMasked >= mFrameCount || frontMasked >= mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700123 return -EIO;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700124 }
125 uint32_t genDiff = (rear & ~mask) - (front & ~mask);
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800126 if (genDiff != 0) {
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700127 if (genDiff > mFrameCountP2) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700128 if (lost != NULL) {
129 // TODO provide a more accurate estimate
130 *lost = (genDiff / mFrameCountP2) * mFrameCount;
131 }
132 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700133 }
134 diff -= mFudgeFactor;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800135 }
136 }
137 // FIFO should not be overfull
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700138 if (diff > mFrameCount) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700139 if (lost != NULL) {
140 *lost = diff - mFrameCount;
141 }
142 return -EOVERFLOW;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700143 }
144 return (int32_t) diff;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800145}
146
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700147////////////////////////////////////////////////////////////////////////////////
148
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700149audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700150 audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
Glenn Kasten09acf782016-06-17 10:28:05 -0700151 __attribute__((no_sanitize("integer"))) :
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700152 audio_utils_fifo_base(frameCount, writerRear, throttleFront),
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700153 mFrameSize(frameSize), mBuffer(buffer)
Glenn Kasten09acf782016-06-17 10:28:05 -0700154{
155 // maximum value of frameCount * frameSize is INT_MAX (2^31 - 1), not 2^31, because we need to
156 // be able to distinguish successful and error return values from read and write.
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700157 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
158 frameCount > ((uint32_t) INT_MAX) / frameSize);
Glenn Kasten09acf782016-06-17 10:28:05 -0700159}
160
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700161audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
162 bool throttlesWriter) :
163 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
164 throttlesWriter ? &mSingleProcessSharedFront : NULL)
165{
166}
167
Glenn Kasten09acf782016-06-17 10:28:05 -0700168audio_utils_fifo::~audio_utils_fifo()
169{
170}
171
172////////////////////////////////////////////////////////////////////////////////
173
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700174audio_utils_fifo_provider::audio_utils_fifo_provider() :
175 mObtained(0)
176{
177}
178
179audio_utils_fifo_provider::~audio_utils_fifo_provider()
180{
181}
182
183////////////////////////////////////////////////////////////////////////////////
184
185audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700186 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700187 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0),
188 mArmed(true), // because initial fill level of zero is < mLowLevelArm
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700189 mEffectiveFrames(fifo.mFrameCount)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700190{
191}
192
193audio_utils_fifo_writer::~audio_utils_fifo_writer()
194{
195}
196
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700197ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count, struct timespec *timeout)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700198 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800199{
Glenn Kasten547a9922016-06-15 13:07:31 -0700200 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700201 ssize_t availToWrite = obtain(iovec, count, timeout);
Glenn Kasten547a9922016-06-15 13:07:31 -0700202 if (availToWrite > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700203 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
204 iovec[0].mLength * mFifo.mFrameSize);
205 if (iovec[1].mLength > 0) {
206 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
207 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
208 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700209 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700210 release(availToWrite);
Glenn Kasten547a9922016-06-15 13:07:31 -0700211 }
212 return availToWrite;
213}
214
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700215// iovec == NULL is not part of the public API, but is used internally to mean don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700216ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
217 struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700218 __attribute__((no_sanitize("integer")))
219{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700220 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700221 size_t availToWrite;
222 if (mFifo.mThrottleFront != NULL) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700223 uint32_t front;
224 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700225 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
226 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700227 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700228 // on error, return an empty slice
229 if (iovec != NULL) {
230 iovec[0].mOffset = 0;
231 iovec[0].mLength = 0;
232 iovec[1].mOffset = 0;
233 iovec[1].mLength = 0;
234 mObtained = 0;
235 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700236 return (ssize_t) filled;
237 }
238 availToWrite = mEffectiveFrames > (uint32_t) filled ?
239 mEffectiveFrames - (uint32_t) filled : 0;
240 // TODO pull out "count == 0"
241 if (count == 0 || availToWrite > 0 || timeout == NULL ||
242 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
243 break;
244 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700245 // TODO add comments
246 // TODO abstract out switch and replace by general sync object
247 int op = FUTEX_WAIT;
248 switch (mFifo.mThrottleFrontSync) {
249 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
250 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
251 if (err < 0) {
252 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
253 err = -errno;
254 } else {
255 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700256 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700257 break;
258 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
259 op = FUTEX_WAIT_PRIVATE;
260 // fall through
261 case AUDIO_UTILS_FIFO_SYNC_SHARED:
262 if (timeout->tv_sec == LONG_MAX) {
263 timeout = NULL;
264 }
265 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
266 if (err < 0) {
267 switch (errno) {
268 case EINTR:
269 case ETIMEDOUT:
270 err = -errno;
271 break;
272 default:
273 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
274 break;
275 }
276 }
277 break;
278 default:
279 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
280 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700281 }
282 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700283 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700284 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700285 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700286 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800287 if (availToWrite > count) {
288 availToWrite = count;
289 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700290 uint32_t rearMasked = mLocalRear & (mFifo.mFrameCountP2 - 1);
291 size_t part1 = mFifo.mFrameCount - rearMasked;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800292 if (part1 > availToWrite) {
293 part1 = availToWrite;
294 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700295 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700296 // return slice
297 if (iovec != NULL) {
298 iovec[0].mOffset = rearMasked;
299 iovec[0].mLength = part1;
300 iovec[1].mOffset = 0;
301 iovec[1].mLength = part2;
302 mObtained = availToWrite;
303 }
304 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800305}
306
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700307void audio_utils_fifo_writer::release(size_t count)
308 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700309{
310 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700311 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700312 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700313 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700314 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700315 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700316 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700317 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700318 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700319 // TODO add comments
320 int op = FUTEX_WAKE;
321 switch (mFifo.mWriterRearSync) {
322 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
323 break;
324 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
325 op = FUTEX_WAKE_PRIVATE;
326 // fall through
327 case AUDIO_UTILS_FIFO_SYNC_SHARED:
328 if (filled >= 0) {
329 if ((uint32_t) filled < mLowLevelArm) {
330 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700331 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700332 if (mArmed && filled + count > mHighLevelTrigger) {
333 int err = sys_futex(&mFifo.mWriterRear.mIndex,
334 op, INT_MAX /*waiters*/, NULL, NULL, 0);
335 // err is number of processes woken up
336 if (err < 0) {
337 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
338 __func__, err, errno);
339 }
340 mArmed = false;
341 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700342 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700343 break;
344 default:
345 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
346 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700347 }
348 } else {
349 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700350 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700351 std::memory_order_release);
352 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700353 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700354 }
355}
356
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700357ssize_t audio_utils_fifo_writer::available()
358{
359 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
360}
361
362void audio_utils_fifo_writer::resize(uint32_t frameCount)
363{
364 // cap to range [0, mFifo.mFrameCount]
365 if (frameCount > mFifo.mFrameCount) {
366 frameCount = mFifo.mFrameCount;
367 }
368 // if we reduce the effective frame count, update hysteresis points to be within the new range
369 if (frameCount < mEffectiveFrames) {
370 if (mLowLevelArm > frameCount) {
371 mLowLevelArm = frameCount;
372 }
373 if (mHighLevelTrigger > frameCount) {
374 mHighLevelTrigger = frameCount;
375 }
376 }
377 mEffectiveFrames = frameCount;
378}
379
380uint32_t audio_utils_fifo_writer::getSize() const
381{
382 return mEffectiveFrames;
383}
384
385void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
386{
387 // cap to range [0, mEffectiveFrames]
388 if (lowLevelArm > mEffectiveFrames) {
389 lowLevelArm = mEffectiveFrames;
390 }
391 if (highLevelTrigger > mEffectiveFrames) {
392 highLevelTrigger = mEffectiveFrames;
393 }
394 // TODO this is overly conservative; it would be better to arm based on actual fill level
395 if (lowLevelArm > mLowLevelArm) {
396 mArmed = true;
397 }
398 mLowLevelArm = lowLevelArm;
399 mHighLevelTrigger = highLevelTrigger;
400}
401
402void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
403{
404 *lowLevelArm = mLowLevelArm;
405 *highLevelTrigger = mHighLevelTrigger;
406}
407
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700408////////////////////////////////////////////////////////////////////////////////
409
410audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700411 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
412 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700413 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
414 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700415{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700416}
417
418audio_utils_fifo_reader::~audio_utils_fifo_reader()
419{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700420 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700421}
422
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700423ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, struct timespec *timeout,
424 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700425 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800426{
Glenn Kasten547a9922016-06-15 13:07:31 -0700427 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700428 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700429 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700430 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
431 iovec[0].mLength * mFifo.mFrameSize);
432 if (iovec[1].mLength > 0) {
433 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
434 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
435 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700436 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700437 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700438 }
439 return availToRead;
440}
441
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700442ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
443 struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700444 __attribute__((no_sanitize("integer")))
445{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700446 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700447}
448
449void audio_utils_fifo_reader::release(size_t count)
450 __attribute__((no_sanitize("integer")))
451{
452 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700453 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700454 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700455 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700456 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700457 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700458 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700459 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700460 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700461 // TODO add comments
462 int op = FUTEX_WAKE;
463 switch (mFifo.mThrottleFrontSync) {
464 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
465 break;
466 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
467 op = FUTEX_WAKE_PRIVATE;
468 // fall through
469 case AUDIO_UTILS_FIFO_SYNC_SHARED:
470 if (filled >= 0) {
471 if (filled > mHighLevelArm) {
472 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700473 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700474 if (mArmed && filled - count < mLowLevelTrigger) {
475 int err = sys_futex(&mThrottleFront->mIndex,
476 op, 1 /*waiters*/, NULL, NULL, 0);
477 // err is number of processes woken up
478 if (err < 0 || err > 1) {
479 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
480 __func__, err, errno);
481 }
482 mArmed = false;
483 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700484 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700485 break;
486 default:
487 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
488 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700489 }
490 } else {
491 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700492 }
493 mObtained -= count;
494 }
495}
496
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700497// iovec == NULL is not part of the public API, but is used internally to mean don't set mObtained
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700498ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
499 struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700500 __attribute__((no_sanitize("integer")))
501{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700502 int err = 0;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700503 uint32_t rear;
504 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700505 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700506 std::memory_order_acquire);
507 // TODO pull out "count == 0"
508 if (count == 0 || rear != mLocalFront || timeout == NULL ||
509 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
510 break;
511 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700512 // TODO add comments
513 int op = FUTEX_WAIT;
514 switch (mFifo.mWriterRearSync) {
515 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
516 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
517 if (err < 0) {
518 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
519 err = -errno;
520 } else {
521 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700522 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700523 break;
524 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
525 op = FUTEX_WAIT_PRIVATE;
526 // fall through
527 case AUDIO_UTILS_FIFO_SYNC_SHARED:
528 if (timeout->tv_sec == LONG_MAX) {
529 timeout = NULL;
530 }
531 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
532 if (err < 0) {
533 switch (errno) {
534 case EINTR:
535 case ETIMEDOUT:
536 err = -errno;
537 break;
538 default:
539 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
540 break;
541 }
542 }
543 break;
544 default:
545 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
546 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700547 }
548 timeout = NULL;
549 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700550 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700551 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700552 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700553 mLocalFront = rear;
554 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700555 // on error, return an empty slice
556 if (iovec != NULL) {
557 iovec[0].mOffset = 0;
558 iovec[0].mLength = 0;
559 iovec[1].mOffset = 0;
560 iovec[1].mLength = 0;
561 mObtained = 0;
562 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700563 return (ssize_t) filled;
564 }
565 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800566 if (availToRead > count) {
567 availToRead = count;
568 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700569 uint32_t frontMasked = mLocalFront & (mFifo.mFrameCountP2 - 1);
570 size_t part1 = mFifo.mFrameCount - frontMasked;
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) {
577 iovec[0].mOffset = frontMasked;
578 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{
593 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
594}
595
596void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
597{
598 // cap to range [0, mFifo.mFrameCount]
599 if (highLevelArm < 0) {
600 highLevelArm = -1;
601 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
602 highLevelArm = mFifo.mFrameCount;
603 }
604 if (lowLevelTrigger > mFifo.mFrameCount) {
605 lowLevelTrigger = mFifo.mFrameCount;
606 }
607 // TODO this is overly conservative; it would be better to arm based on actual fill level
608 if (highLevelArm < mHighLevelArm) {
609 mArmed = true;
610 }
611 mHighLevelArm = highLevelArm;
612 mLowLevelTrigger = lowLevelTrigger;
613}
614
615void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
616{
617 *highLevelArm = mHighLevelArm;
618 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700619}