blob: a148db6e4ec5e099e07da3f22a653a2a368c5e94 [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 Kasten0ab1d862016-06-20 12:04:56 -070096 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT_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;
127 uint32_t rearMasked = rear & mask;
128 uint32_t frontMasked = front & mask;
129 if (rearMasked >= mFrameCount || frontMasked >= 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{
162 // maximum value of frameCount * frameSize is INT_MAX (2^31 - 1), not 2^31, because we need to
163 // 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 ||
165 frameCount > ((uint32_t) INT_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 Kastenc0924bc2016-10-02 13:00:19 -0700223// 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 -0700224ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700225 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700226 __attribute__((no_sanitize("integer")))
227{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700228 int err = 0;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700229 size_t availToWrite;
230 if (mFifo.mThrottleFront != NULL) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700231 uint32_t front;
232 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700233 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
234 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700235 if (filled < 0) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700236 // on error, return an empty slice
237 if (iovec != NULL) {
238 iovec[0].mOffset = 0;
239 iovec[0].mLength = 0;
240 iovec[1].mOffset = 0;
241 iovec[1].mLength = 0;
242 mObtained = 0;
243 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700244 return (ssize_t) filled;
245 }
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
255 int op = FUTEX_WAIT;
256 switch (mFifo.mThrottleFrontSync) {
257 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
258 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
259 if (err < 0) {
260 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
261 err = -errno;
262 } else {
263 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700264 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700265 break;
266 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
267 op = FUTEX_WAIT_PRIVATE;
268 // fall through
269 case AUDIO_UTILS_FIFO_SYNC_SHARED:
270 if (timeout->tv_sec == LONG_MAX) {
271 timeout = NULL;
272 }
273 err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
274 if (err < 0) {
275 switch (errno) {
276 case EINTR:
277 case ETIMEDOUT:
278 err = -errno;
279 break;
280 default:
281 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
282 break;
283 }
284 }
285 break;
286 default:
287 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
288 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700289 }
290 timeout = NULL;
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700291 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700292 } else {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700293 availToWrite = mEffectiveFrames;
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700294 }
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800295 if (availToWrite > count) {
296 availToWrite = count;
297 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700298 uint32_t rearMasked = mLocalRear & (mFifo.mFrameCountP2 - 1);
299 size_t part1 = mFifo.mFrameCount - rearMasked;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800300 if (part1 > availToWrite) {
301 part1 = availToWrite;
302 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700303 size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700304 // return slice
305 if (iovec != NULL) {
306 iovec[0].mOffset = rearMasked;
307 iovec[0].mLength = part1;
308 iovec[1].mOffset = 0;
309 iovec[1].mLength = part2;
310 mObtained = availToWrite;
311 }
312 return availToWrite > 0 ? availToWrite : err;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800313}
314
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700315void audio_utils_fifo_writer::release(size_t count)
316 __attribute__((no_sanitize("integer")))
Glenn Kasten547a9922016-06-15 13:07:31 -0700317{
318 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700319 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700320 if (mFifo.mThrottleFront != NULL) {
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700321 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700322 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700323 int32_t filled = mFifo.diff(mLocalRear, front);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700324 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700325 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700326 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700327 // TODO add comments
328 int op = FUTEX_WAKE;
329 switch (mFifo.mWriterRearSync) {
330 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
331 break;
332 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
333 op = FUTEX_WAKE_PRIVATE;
334 // fall through
335 case AUDIO_UTILS_FIFO_SYNC_SHARED:
336 if (filled >= 0) {
337 if ((uint32_t) filled < mLowLevelArm) {
338 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700339 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700340 if (mArmed && filled + count > mHighLevelTrigger) {
341 int err = sys_futex(&mFifo.mWriterRear.mIndex,
342 op, INT_MAX /*waiters*/, NULL, NULL, 0);
343 // err is number of processes woken up
344 if (err < 0) {
345 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
346 __func__, err, errno);
347 }
348 mArmed = false;
349 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700350 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700351 break;
352 default:
353 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
354 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700355 }
356 } else {
357 mLocalRear = mFifo.sum(mLocalRear, count);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700358 atomic_store_explicit(&mFifo.mWriterRear.mIndex, mLocalRear,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700359 std::memory_order_release);
360 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700361 mObtained -= count;
Glenn Kasten547a9922016-06-15 13:07:31 -0700362 }
363}
364
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700365ssize_t audio_utils_fifo_writer::available()
366{
367 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
368}
369
370void audio_utils_fifo_writer::resize(uint32_t frameCount)
371{
372 // cap to range [0, mFifo.mFrameCount]
373 if (frameCount > mFifo.mFrameCount) {
374 frameCount = mFifo.mFrameCount;
375 }
376 // if we reduce the effective frame count, update hysteresis points to be within the new range
377 if (frameCount < mEffectiveFrames) {
378 if (mLowLevelArm > frameCount) {
379 mLowLevelArm = frameCount;
380 }
381 if (mHighLevelTrigger > frameCount) {
382 mHighLevelTrigger = frameCount;
383 }
384 }
385 mEffectiveFrames = frameCount;
386}
387
388uint32_t audio_utils_fifo_writer::getSize() const
389{
390 return mEffectiveFrames;
391}
392
393void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
394{
395 // cap to range [0, mEffectiveFrames]
396 if (lowLevelArm > mEffectiveFrames) {
397 lowLevelArm = mEffectiveFrames;
398 }
399 if (highLevelTrigger > mEffectiveFrames) {
400 highLevelTrigger = mEffectiveFrames;
401 }
402 // TODO this is overly conservative; it would be better to arm based on actual fill level
403 if (lowLevelArm > mLowLevelArm) {
404 mArmed = true;
405 }
406 mLowLevelArm = lowLevelArm;
407 mHighLevelTrigger = highLevelTrigger;
408}
409
410void audio_utils_fifo_writer::getHysteresis(uint32_t *lowLevelArm, uint32_t *highLevelTrigger) const
411{
412 *lowLevelArm = mLowLevelArm;
413 *highLevelTrigger = mHighLevelTrigger;
414}
415
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700416////////////////////////////////////////////////////////////////////////////////
417
418audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700419 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0),
420 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700421 mHighLevelArm(-1), mLowLevelTrigger(mFifo.mFrameCount),
422 mArmed(true) // because initial fill level of zero is > mHighLevelArm
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700423{
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700424}
425
426audio_utils_fifo_reader::~audio_utils_fifo_reader()
427{
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700428 // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700429}
430
Glenn Kastend9942f72016-10-16 14:51:15 -0700431ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700432 size_t *lost)
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700433 __attribute__((no_sanitize("integer")))
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800434{
Glenn Kasten547a9922016-06-15 13:07:31 -0700435 audio_utils_iovec iovec[2];
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700436 ssize_t availToRead = obtain(iovec, count, timeout, lost);
Glenn Kasten547a9922016-06-15 13:07:31 -0700437 if (availToRead > 0) {
Glenn Kasten169f3a22016-06-17 10:43:34 -0700438 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
439 iovec[0].mLength * mFifo.mFrameSize);
440 if (iovec[1].mLength > 0) {
441 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
442 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
443 iovec[1].mLength * mFifo.mFrameSize);
Glenn Kasten547a9922016-06-15 13:07:31 -0700444 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700445 release(availToRead);
Glenn Kasten547a9922016-06-15 13:07:31 -0700446 }
447 return availToRead;
448}
449
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700450ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700451 const struct timespec *timeout)
Glenn Kasten547a9922016-06-15 13:07:31 -0700452 __attribute__((no_sanitize("integer")))
453{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700454 return obtain(iovec, count, timeout, NULL /*lost*/);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700455}
456
457void audio_utils_fifo_reader::release(size_t count)
458 __attribute__((no_sanitize("integer")))
459{
460 if (count > 0) {
Glenn Kasten0ab1d862016-06-20 12:04:56 -0700461 LOG_ALWAYS_FATAL_IF(count > mObtained);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700462 if (mThrottleFront != NULL) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700463 uint32_t rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700464 std::memory_order_acquire);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700465 int32_t filled = mFifo.diff(rear, mLocalFront);
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700466 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kastendc1ff1f2016-09-02 13:54:59 -0700467 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront,
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700468 std::memory_order_release);
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700469 // TODO add comments
470 int op = FUTEX_WAKE;
471 switch (mFifo.mThrottleFrontSync) {
472 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
473 break;
474 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
475 op = FUTEX_WAKE_PRIVATE;
476 // fall through
477 case AUDIO_UTILS_FIFO_SYNC_SHARED:
478 if (filled >= 0) {
479 if (filled > mHighLevelArm) {
480 mArmed = true;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700481 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700482 if (mArmed && filled - count < mLowLevelTrigger) {
483 int err = sys_futex(&mThrottleFront->mIndex,
484 op, 1 /*waiters*/, NULL, NULL, 0);
485 // err is number of processes woken up
486 if (err < 0 || err > 1) {
487 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
488 __func__, err, errno);
489 }
490 mArmed = false;
491 }
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700492 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700493 break;
494 default:
495 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
496 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700497 }
498 } else {
499 mLocalFront = mFifo.sum(mLocalFront, count);
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700500 }
501 mObtained -= count;
502 }
503}
504
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700505// 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 -0700506ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
Glenn Kastend9942f72016-10-16 14:51:15 -0700507 const struct timespec *timeout, size_t *lost)
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700508 __attribute__((no_sanitize("integer")))
509{
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700510 int err = 0;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700511 uint32_t rear;
512 for (;;) {
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700513 rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700514 std::memory_order_acquire);
515 // TODO pull out "count == 0"
516 if (count == 0 || rear != mLocalFront || timeout == NULL ||
517 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
518 break;
519 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700520 // TODO add comments
521 int op = FUTEX_WAIT;
522 switch (mFifo.mWriterRearSync) {
523 case AUDIO_UTILS_FIFO_SYNC_SLEEP:
524 err = clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, NULL /*remain*/);
525 if (err < 0) {
526 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
527 err = -errno;
528 } else {
529 err = -ETIMEDOUT;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700530 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700531 break;
532 case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
533 op = FUTEX_WAIT_PRIVATE;
534 // fall through
535 case AUDIO_UTILS_FIFO_SYNC_SHARED:
536 if (timeout->tv_sec == LONG_MAX) {
537 timeout = NULL;
538 }
539 err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
540 if (err < 0) {
541 switch (errno) {
542 case EINTR:
543 case ETIMEDOUT:
544 err = -errno;
545 break;
546 default:
547 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
548 break;
549 }
550 }
551 break;
552 default:
553 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
554 break;
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700555 }
556 timeout = NULL;
557 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700558 int32_t filled = mFifo.diff(rear, mLocalFront, lost);
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700559 if (filled < 0) {
Glenn Kasten3ddacbb2016-06-20 14:41:03 -0700560 if (filled == -EOVERFLOW) {
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700561 mLocalFront = rear;
562 }
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700563 // on error, return an empty slice
564 if (iovec != NULL) {
565 iovec[0].mOffset = 0;
566 iovec[0].mLength = 0;
567 iovec[1].mOffset = 0;
568 iovec[1].mLength = 0;
569 mObtained = 0;
570 }
Glenn Kasten9b4fe472016-06-13 09:34:57 -0700571 return (ssize_t) filled;
572 }
573 size_t availToRead = (size_t) filled;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800574 if (availToRead > count) {
575 availToRead = count;
576 }
Glenn Kasten6d7ad762016-06-15 17:05:54 -0700577 uint32_t frontMasked = mLocalFront & (mFifo.mFrameCountP2 - 1);
578 size_t part1 = mFifo.mFrameCount - frontMasked;
Glenn Kasten9b4c8052015-01-06 14:13:13 -0800579 if (part1 > availToRead) {
580 part1 = availToRead;
581 }
Glenn Kasten547a9922016-06-15 13:07:31 -0700582 size_t part2 = part1 > 0 ? availToRead - part1 : 0;
Glenn Kastenc0924bc2016-10-02 13:00:19 -0700583 // return slice
584 if (iovec != NULL) {
585 iovec[0].mOffset = frontMasked;
586 iovec[0].mLength = part1;
587 iovec[1].mOffset = 0;
588 iovec[1].mLength = part2;
589 mObtained = availToRead;
590 }
591 return availToRead > 0 ? availToRead : err;
592}
593
594ssize_t audio_utils_fifo_reader::available()
595{
596 return available(NULL /*lost*/);
597}
598
599ssize_t audio_utils_fifo_reader::available(size_t *lost)
600{
601 return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
602}
603
604void audio_utils_fifo_reader::setHysteresis(int32_t highLevelArm, uint32_t lowLevelTrigger)
605{
606 // cap to range [0, mFifo.mFrameCount]
607 if (highLevelArm < 0) {
608 highLevelArm = -1;
609 } else if ((uint32_t) highLevelArm > mFifo.mFrameCount) {
610 highLevelArm = mFifo.mFrameCount;
611 }
612 if (lowLevelTrigger > mFifo.mFrameCount) {
613 lowLevelTrigger = mFifo.mFrameCount;
614 }
615 // TODO this is overly conservative; it would be better to arm based on actual fill level
616 if (highLevelArm < mHighLevelArm) {
617 mArmed = true;
618 }
619 mHighLevelArm = highLevelArm;
620 mLowLevelTrigger = lowLevelTrigger;
621}
622
623void audio_utils_fifo_reader::getHysteresis(int32_t *highLevelArm, uint32_t *lowLevelTrigger) const
624{
625 *highLevelArm = mHighLevelArm;
626 *lowLevelTrigger = mLowLevelTrigger;
Glenn Kasten547a9922016-06-15 13:07:31 -0700627}