Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 1 | /* |
| 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 Kasten | 9052f3b | 2016-07-08 16:24:41 -0700 | [diff] [blame] | 20 | #include <errno.h> |
Glenn Kasten | 0ab1d86 | 2016-06-20 12:04:56 -0700 | [diff] [blame] | 21 | #include <limits.h> |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
Glenn Kasten | 9052f3b | 2016-07-08 16:24:41 -0700 | [diff] [blame] | 24 | |
Glenn Kasten | be9f4d8 | 2016-12-01 08:30:21 -0800 | [diff] [blame] | 25 | #include <audio_utils/clock_nanosleep.h> |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 26 | #include <audio_utils/fifo.h> |
Glenn Kasten | be9f4d8 | 2016-12-01 08:30:21 -0800 | [diff] [blame] | 27 | #include <audio_utils/futex.h> |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 28 | #include <audio_utils/roundup.h> |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 29 | #include <cutils/log.h> |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 30 | #include <utils/Errors.h> |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 31 | |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 32 | audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount, |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 33 | audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront) |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 34 | __attribute__((no_sanitize("integer"))) : |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 35 | mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)), |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 36 | mFudgeFactor(mFrameCountP2 - mFrameCount), |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 37 | // FIXME need an API to configure the sync types |
| 38 | mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED), |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 39 | mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED), |
| 40 | mIsShutdown(false) |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 41 | { |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 42 | // actual upper bound on frameCount will depend on the frame size |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 43 | LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX)); |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 44 | } |
| 45 | |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 46 | audio_utils_fifo_base::~audio_utils_fifo_base() |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 47 | { |
| 48 | } |
| 49 | |
Glenn Kasten | 3f115ba | 2016-11-22 14:00:53 -0800 | [diff] [blame] | 50 | uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 51 | __attribute__((no_sanitize("integer"))) |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 52 | { |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 53 | if (mFudgeFactor) { |
| 54 | uint32_t mask = mFrameCountP2 - 1; |
| 55 | ALOG_ASSERT((index & mask) < mFrameCount); |
| 56 | ALOG_ASSERT(increment <= mFrameCountP2); |
| 57 | if ((index & mask) + increment >= mFrameCount) { |
| 58 | increment += mFudgeFactor; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 59 | } |
| 60 | index += increment; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 61 | ALOG_ASSERT((index & mask) < mFrameCount); |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 62 | return index; |
| 63 | } else { |
| 64 | return index + increment; |
| 65 | } |
| 66 | } |
| 67 | |
Glenn Kasten | 3f115ba | 2016-11-22 14:00:53 -0800 | [diff] [blame] | 68 | int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) const |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 69 | __attribute__((no_sanitize("integer"))) |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 70 | { |
Glenn Kasten | 44001d4 | 2016-10-19 15:46:02 -0700 | [diff] [blame] | 71 | // TODO replace multiple returns by a single return point so this isn't needed |
| 72 | if (lost != NULL) { |
| 73 | *lost = 0; |
| 74 | } |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 75 | if (mIsShutdown) { |
| 76 | return -EIO; |
| 77 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 78 | uint32_t diff = rear - front; |
| 79 | if (mFudgeFactor) { |
| 80 | uint32_t mask = mFrameCountP2 - 1; |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 81 | uint32_t rearOffset = rear & mask; |
| 82 | uint32_t frontOffset = front & mask; |
| 83 | if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 84 | ALOGE("%s frontOffset=%u rearOffset=%u mFrameCount=%u", |
| 85 | __func__, frontOffset, rearOffset, mFrameCount); |
| 86 | shutdown(); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 87 | return -EIO; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 88 | } |
| 89 | uint32_t genDiff = (rear & ~mask) - (front & ~mask); |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 90 | if (genDiff != 0) { |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 91 | if (genDiff > mFrameCountP2) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 92 | if (lost != NULL) { |
| 93 | // TODO provide a more accurate estimate |
| 94 | *lost = (genDiff / mFrameCountP2) * mFrameCount; |
| 95 | } |
| 96 | return -EOVERFLOW; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 97 | } |
| 98 | diff -= mFudgeFactor; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 99 | } |
| 100 | } |
| 101 | // FIFO should not be overfull |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 102 | if (diff > mFrameCount) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 103 | if (lost != NULL) { |
Glenn Kasten | 44001d4 | 2016-10-19 15:46:02 -0700 | [diff] [blame] | 104 | *lost = diff; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 105 | } |
| 106 | return -EOVERFLOW; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 107 | } |
| 108 | return (int32_t) diff; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 109 | } |
| 110 | |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 111 | void audio_utils_fifo_base::shutdown() const |
| 112 | { |
| 113 | ALOGE("%s", __func__); |
| 114 | mIsShutdown = true; |
| 115 | } |
| 116 | |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 117 | //////////////////////////////////////////////////////////////////////////////// |
| 118 | |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 119 | audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer, |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 120 | audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront) |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 121 | __attribute__((no_sanitize("integer"))) : |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 122 | audio_utils_fifo_base(frameCount, writerRear, throttleFront), |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 123 | mFrameSize(frameSize), mBuffer(buffer) |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 124 | { |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 125 | // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 126 | // be able to distinguish successful and error return values from read and write. |
Glenn Kasten | 0ab1d86 | 2016-06-20 12:04:56 -0700 | [diff] [blame] | 127 | LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL || |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 128 | frameCount > ((uint32_t) INT32_MAX) / frameSize); |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 129 | } |
| 130 | |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 131 | audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer, |
| 132 | bool throttlesWriter) : |
| 133 | audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear, |
| 134 | throttlesWriter ? &mSingleProcessSharedFront : NULL) |
| 135 | { |
| 136 | } |
| 137 | |
Glenn Kasten | 09acf78 | 2016-06-17 10:28:05 -0700 | [diff] [blame] | 138 | audio_utils_fifo::~audio_utils_fifo() |
| 139 | { |
| 140 | } |
| 141 | |
| 142 | //////////////////////////////////////////////////////////////////////////////// |
| 143 | |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 144 | audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) : |
| 145 | mFifo(fifo), mObtained(0), mTotalReleased(0) |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 146 | { |
| 147 | } |
| 148 | |
| 149 | audio_utils_fifo_provider::~audio_utils_fifo_provider() |
| 150 | { |
| 151 | } |
| 152 | |
| 153 | //////////////////////////////////////////////////////////////////////////////// |
| 154 | |
| 155 | audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) : |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 156 | audio_utils_fifo_provider(fifo), mLocalRear(0), |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 157 | mArmLevel(fifo.mFrameCount), mTriggerLevel(0), |
| 158 | mIsArmed(true), // because initial fill level of zero is < mArmLevel |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 159 | mEffectiveFrames(fifo.mFrameCount) |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 160 | { |
| 161 | } |
| 162 | |
| 163 | audio_utils_fifo_writer::~audio_utils_fifo_writer() |
| 164 | { |
| 165 | } |
| 166 | |
Glenn Kasten | d9942f7 | 2016-10-16 14:51:15 -0700 | [diff] [blame] | 167 | ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count, |
| 168 | const struct timespec *timeout) |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 169 | __attribute__((no_sanitize("integer"))) |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 170 | { |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 171 | audio_utils_iovec iovec[2]; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 172 | ssize_t availToWrite = obtain(iovec, count, timeout); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 173 | if (availToWrite > 0) { |
Glenn Kasten | 169f3a2 | 2016-06-17 10:43:34 -0700 | [diff] [blame] | 174 | memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer, |
| 175 | iovec[0].mLength * mFifo.mFrameSize); |
| 176 | if (iovec[1].mLength > 0) { |
| 177 | memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize, |
| 178 | (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize), |
| 179 | iovec[1].mLength * mFifo.mFrameSize); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 180 | } |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 181 | release(availToWrite); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 182 | } |
| 183 | return availToWrite; |
| 184 | } |
| 185 | |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 186 | // iovec == NULL is not part of the public API, but internally it means don't set mObtained |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 187 | ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count, |
Glenn Kasten | d9942f7 | 2016-10-16 14:51:15 -0700 | [diff] [blame] | 188 | const struct timespec *timeout) |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 189 | __attribute__((no_sanitize("integer"))) |
| 190 | { |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 191 | int err = 0; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 192 | size_t availToWrite; |
| 193 | if (mFifo.mThrottleFront != NULL) { |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 194 | int retries = kRetries; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 195 | uint32_t front; |
| 196 | for (;;) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 197 | front = mFifo.mThrottleFront->loadAcquire(); |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 198 | // returns -EIO if mIsShutdown |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 199 | int32_t filled = mFifo.diff(mLocalRear, front); |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 200 | if (filled < 0) { |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 201 | // on error, return an empty slice |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 202 | err = filled; |
| 203 | availToWrite = 0; |
| 204 | break; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 205 | } |
| 206 | availToWrite = mEffectiveFrames > (uint32_t) filled ? |
| 207 | mEffectiveFrames - (uint32_t) filled : 0; |
| 208 | // TODO pull out "count == 0" |
| 209 | if (count == 0 || availToWrite > 0 || timeout == NULL || |
| 210 | (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) { |
| 211 | break; |
| 212 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 213 | // TODO add comments |
| 214 | // TODO abstract out switch and replace by general sync object |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 215 | // the high level code (synchronization, sleep, futex, iovec) should be completely |
| 216 | // separate from the low level code (indexes, available, masking). |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 217 | int op = FUTEX_WAIT; |
| 218 | switch (mFifo.mThrottleFrontSync) { |
| 219 | case AUDIO_UTILS_FIFO_SYNC_SLEEP: |
Glenn Kasten | be9f4d8 | 2016-12-01 08:30:21 -0800 | [diff] [blame] | 220 | err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, |
| 221 | NULL /*remain*/); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 222 | if (err < 0) { |
| 223 | LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno); |
| 224 | err = -errno; |
| 225 | } else { |
| 226 | err = -ETIMEDOUT; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 227 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 228 | break; |
| 229 | case AUDIO_UTILS_FIFO_SYNC_PRIVATE: |
| 230 | op = FUTEX_WAIT_PRIVATE; |
| 231 | // fall through |
| 232 | case AUDIO_UTILS_FIFO_SYNC_SHARED: |
| 233 | if (timeout->tv_sec == LONG_MAX) { |
| 234 | timeout = NULL; |
| 235 | } |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 236 | err = mFifo.mThrottleFront->wait(op, front, timeout); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 237 | if (err < 0) { |
| 238 | switch (errno) { |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 239 | case EWOULDBLOCK: |
Glenn Kasten | 7cabff9 | 2016-10-19 11:13:55 -0700 | [diff] [blame] | 240 | // Benign race condition with partner: mFifo.mThrottleFront->mIndex |
| 241 | // changed value between the earlier atomic_load_explicit() and sys_futex(). |
| 242 | // Try to load index again, but give up if we are unable to converge. |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 243 | if (retries-- > 0) { |
| 244 | // bypass the "timeout = NULL;" below |
| 245 | continue; |
| 246 | } |
| 247 | // fall through |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 248 | case EINTR: |
| 249 | case ETIMEDOUT: |
| 250 | err = -errno; |
| 251 | break; |
| 252 | default: |
| 253 | LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno); |
| 254 | break; |
| 255 | } |
| 256 | } |
| 257 | break; |
| 258 | default: |
| 259 | LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync); |
| 260 | break; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 261 | } |
| 262 | timeout = NULL; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 263 | } |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 264 | } else { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 265 | if (mFifo.mIsShutdown) { |
| 266 | err = -EIO; |
| 267 | availToWrite = 0; |
| 268 | } else { |
| 269 | availToWrite = mEffectiveFrames; |
| 270 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 271 | } |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 272 | if (availToWrite > count) { |
| 273 | availToWrite = count; |
| 274 | } |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 275 | uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1); |
| 276 | size_t part1 = mFifo.mFrameCount - rearOffset; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 277 | if (part1 > availToWrite) { |
| 278 | part1 = availToWrite; |
| 279 | } |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 280 | size_t part2 = part1 > 0 ? availToWrite - part1 : 0; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 281 | // return slice |
| 282 | if (iovec != NULL) { |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 283 | iovec[0].mOffset = rearOffset; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 284 | iovec[0].mLength = part1; |
| 285 | iovec[1].mOffset = 0; |
| 286 | iovec[1].mLength = part2; |
| 287 | mObtained = availToWrite; |
| 288 | } |
| 289 | return availToWrite > 0 ? availToWrite : err; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 290 | } |
| 291 | |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 292 | void audio_utils_fifo_writer::release(size_t count) |
| 293 | __attribute__((no_sanitize("integer"))) |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 294 | { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 295 | // no need to do an early check for mIsShutdown, because the extra code executed is harmless |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 296 | if (count > 0) { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 297 | if (count > mObtained) { |
| 298 | ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained); |
| 299 | mFifo.shutdown(); |
| 300 | return; |
| 301 | } |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 302 | if (mFifo.mThrottleFront != NULL) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 303 | uint32_t front = mFifo.mThrottleFront->loadAcquire(); |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 304 | // returns -EIO if mIsShutdown |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 305 | int32_t filled = mFifo.diff(mLocalRear, front); |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 306 | mLocalRear = mFifo.sum(mLocalRear, count); |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 307 | mFifo.mWriterRear.storeRelease(mLocalRear); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 308 | // TODO add comments |
| 309 | int op = FUTEX_WAKE; |
| 310 | switch (mFifo.mWriterRearSync) { |
| 311 | case AUDIO_UTILS_FIFO_SYNC_SLEEP: |
| 312 | break; |
| 313 | case AUDIO_UTILS_FIFO_SYNC_PRIVATE: |
| 314 | op = FUTEX_WAKE_PRIVATE; |
| 315 | // fall through |
| 316 | case AUDIO_UTILS_FIFO_SYNC_SHARED: |
| 317 | if (filled >= 0) { |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 318 | if ((uint32_t) filled < mArmLevel) { |
| 319 | mIsArmed = true; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 320 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 321 | if (mIsArmed && filled + count > mTriggerLevel) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 322 | int err = mFifo.mWriterRear.wake(op, INT32_MAX /*waiters*/); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 323 | // err is number of processes woken up |
| 324 | if (err < 0) { |
| 325 | LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", |
| 326 | __func__, err, errno); |
| 327 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 328 | mIsArmed = false; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 329 | } |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 330 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 331 | break; |
| 332 | default: |
| 333 | LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync); |
| 334 | break; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 335 | } |
| 336 | } else { |
| 337 | mLocalRear = mFifo.sum(mLocalRear, count); |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 338 | mFifo.mWriterRear.storeRelease(mLocalRear); |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 339 | } |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 340 | mObtained -= count; |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 341 | mTotalReleased += count; |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 342 | } |
| 343 | } |
| 344 | |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 345 | ssize_t audio_utils_fifo_writer::available() |
| 346 | { |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 347 | // iovec == NULL is not part of the public API, but internally it means don't set mObtained |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 348 | return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/); |
| 349 | } |
| 350 | |
| 351 | void audio_utils_fifo_writer::resize(uint32_t frameCount) |
| 352 | { |
| 353 | // cap to range [0, mFifo.mFrameCount] |
| 354 | if (frameCount > mFifo.mFrameCount) { |
| 355 | frameCount = mFifo.mFrameCount; |
| 356 | } |
| 357 | // if we reduce the effective frame count, update hysteresis points to be within the new range |
| 358 | if (frameCount < mEffectiveFrames) { |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 359 | if (mArmLevel > frameCount) { |
| 360 | mArmLevel = frameCount; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 361 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 362 | if (mTriggerLevel > frameCount) { |
| 363 | mTriggerLevel = frameCount; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 364 | } |
| 365 | } |
| 366 | mEffectiveFrames = frameCount; |
| 367 | } |
| 368 | |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 369 | uint32_t audio_utils_fifo_writer::size() const |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 370 | { |
| 371 | return mEffectiveFrames; |
| 372 | } |
| 373 | |
| 374 | void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger) |
| 375 | { |
| 376 | // cap to range [0, mEffectiveFrames] |
| 377 | if (lowLevelArm > mEffectiveFrames) { |
| 378 | lowLevelArm = mEffectiveFrames; |
| 379 | } |
| 380 | if (highLevelTrigger > mEffectiveFrames) { |
| 381 | highLevelTrigger = mEffectiveFrames; |
| 382 | } |
| 383 | // TODO this is overly conservative; it would be better to arm based on actual fill level |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 384 | if (lowLevelArm > mArmLevel) { |
| 385 | mIsArmed = true; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 386 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 387 | mArmLevel = lowLevelArm; |
| 388 | mTriggerLevel = highLevelTrigger; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 389 | } |
| 390 | |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 391 | void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 392 | { |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 393 | *armLevel = mArmLevel; |
| 394 | *triggerLevel = mTriggerLevel; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 395 | } |
| 396 | |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 397 | //////////////////////////////////////////////////////////////////////////////// |
| 398 | |
| 399 | audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) : |
Glenn Kasten | 2f99531 | 2016-12-16 12:42:26 -0800 | [diff] [blame] | 400 | audio_utils_fifo_provider(fifo), |
| 401 | |
| 402 | // If we throttle the writer, then initialize our front index to zero so that we see all data |
| 403 | // currently in the buffer. |
| 404 | // Otherwise, ignore everything currently in the buffer by initializing our front index to the |
| 405 | // current value of writer's rear. This avoids an immediate -EOVERFLOW (overrun) in the case |
| 406 | // where reader starts out more than one buffer behind writer. The initial catch-up does not |
| 407 | // contribute towards the totalLost, totalFlushed, or totalReleased counters. |
| 408 | mLocalFront(throttlesWriter ? 0 : mFifo.mWriterRear.loadConsume()), |
| 409 | |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 410 | mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL), |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 411 | mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount), |
| 412 | mIsArmed(true), // because initial fill level of zero is > mArmLevel |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 413 | mTotalLost(0), mTotalFlushed(0) |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 414 | { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 415 | } |
| 416 | |
| 417 | audio_utils_fifo_reader::~audio_utils_fifo_reader() |
| 418 | { |
Glenn Kasten | 0ab1d86 | 2016-06-20 12:04:56 -0700 | [diff] [blame] | 419 | // TODO Need a way to pass throttle capability to the another reader, should one reader exit. |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 420 | } |
| 421 | |
Glenn Kasten | d9942f7 | 2016-10-16 14:51:15 -0700 | [diff] [blame] | 422 | ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout, |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 423 | size_t *lost) |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 424 | __attribute__((no_sanitize("integer"))) |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 425 | { |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 426 | audio_utils_iovec iovec[2]; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 427 | ssize_t availToRead = obtain(iovec, count, timeout, lost); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 428 | if (availToRead > 0) { |
Glenn Kasten | 169f3a2 | 2016-06-17 10:43:34 -0700 | [diff] [blame] | 429 | memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, |
| 430 | iovec[0].mLength * mFifo.mFrameSize); |
| 431 | if (iovec[1].mLength > 0) { |
| 432 | memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize), |
| 433 | (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize, |
| 434 | iovec[1].mLength * mFifo.mFrameSize); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 435 | } |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 436 | release(availToRead); |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 437 | } |
| 438 | return availToRead; |
| 439 | } |
| 440 | |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 441 | ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count, |
Glenn Kasten | d9942f7 | 2016-10-16 14:51:15 -0700 | [diff] [blame] | 442 | const struct timespec *timeout) |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 443 | __attribute__((no_sanitize("integer"))) |
| 444 | { |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 445 | return obtain(iovec, count, timeout, NULL /*lost*/); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 446 | } |
| 447 | |
| 448 | void audio_utils_fifo_reader::release(size_t count) |
| 449 | __attribute__((no_sanitize("integer"))) |
| 450 | { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 451 | // no need to do an early check for mIsShutdown, because the extra code executed is harmless |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 452 | if (count > 0) { |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 453 | if (count > mObtained) { |
| 454 | ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained); |
| 455 | mFifo.shutdown(); |
| 456 | return; |
| 457 | } |
Glenn Kasten | dc1ff1f | 2016-09-02 13:54:59 -0700 | [diff] [blame] | 458 | if (mThrottleFront != NULL) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 459 | uint32_t rear = mFifo.mWriterRear.loadAcquire(); |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 460 | // returns -EIO if mIsShutdown |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 461 | int32_t filled = mFifo.diff(rear, mLocalFront); |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 462 | mLocalFront = mFifo.sum(mLocalFront, count); |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 463 | mThrottleFront->storeRelease(mLocalFront); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 464 | // TODO add comments |
| 465 | int op = FUTEX_WAKE; |
| 466 | switch (mFifo.mThrottleFrontSync) { |
| 467 | case AUDIO_UTILS_FIFO_SYNC_SLEEP: |
| 468 | break; |
| 469 | case AUDIO_UTILS_FIFO_SYNC_PRIVATE: |
| 470 | op = FUTEX_WAKE_PRIVATE; |
| 471 | // fall through |
| 472 | case AUDIO_UTILS_FIFO_SYNC_SHARED: |
| 473 | if (filled >= 0) { |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 474 | if (filled > mArmLevel) { |
| 475 | mIsArmed = true; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 476 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 477 | if (mIsArmed && filled - count < mTriggerLevel) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 478 | int err = mThrottleFront->wake(op, 1 /*waiters*/); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 479 | // err is number of processes woken up |
| 480 | if (err < 0 || err > 1) { |
| 481 | LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", |
| 482 | __func__, err, errno); |
| 483 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 484 | mIsArmed = false; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 485 | } |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 486 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 487 | break; |
| 488 | default: |
| 489 | LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync); |
| 490 | break; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 491 | } |
| 492 | } else { |
| 493 | mLocalFront = mFifo.sum(mLocalFront, count); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 494 | } |
| 495 | mObtained -= count; |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 496 | mTotalReleased += count; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 497 | } |
| 498 | } |
| 499 | |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 500 | // iovec == NULL is not part of the public API, but internally it means don't set mObtained |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 501 | ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count, |
Glenn Kasten | d9942f7 | 2016-10-16 14:51:15 -0700 | [diff] [blame] | 502 | const struct timespec *timeout, size_t *lost) |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 503 | __attribute__((no_sanitize("integer"))) |
| 504 | { |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 505 | int err = 0; |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 506 | int retries = kRetries; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 507 | uint32_t rear; |
| 508 | for (;;) { |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 509 | rear = mFifo.mWriterRear.loadAcquire(); |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 510 | // TODO pull out "count == 0" |
| 511 | if (count == 0 || rear != mLocalFront || timeout == NULL || |
| 512 | (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) { |
| 513 | break; |
| 514 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 515 | // TODO add comments |
| 516 | int op = FUTEX_WAIT; |
| 517 | switch (mFifo.mWriterRearSync) { |
| 518 | case AUDIO_UTILS_FIFO_SYNC_SLEEP: |
Glenn Kasten | be9f4d8 | 2016-12-01 08:30:21 -0800 | [diff] [blame] | 519 | err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout, |
| 520 | NULL /*remain*/); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 521 | if (err < 0) { |
| 522 | LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno); |
| 523 | err = -errno; |
| 524 | } else { |
| 525 | err = -ETIMEDOUT; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 526 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 527 | break; |
| 528 | case AUDIO_UTILS_FIFO_SYNC_PRIVATE: |
| 529 | op = FUTEX_WAIT_PRIVATE; |
| 530 | // fall through |
| 531 | case AUDIO_UTILS_FIFO_SYNC_SHARED: |
| 532 | if (timeout->tv_sec == LONG_MAX) { |
| 533 | timeout = NULL; |
| 534 | } |
Glenn Kasten | 7cc8f54 | 2016-12-01 16:12:59 -0800 | [diff] [blame] | 535 | err = mFifo.mWriterRear.wait(op, rear, timeout); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 536 | if (err < 0) { |
| 537 | switch (errno) { |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 538 | case EWOULDBLOCK: |
Glenn Kasten | 7cabff9 | 2016-10-19 11:13:55 -0700 | [diff] [blame] | 539 | // Benign race condition with partner: mFifo.mWriterRear->mIndex |
| 540 | // changed value between the earlier atomic_load_explicit() and sys_futex(). |
| 541 | // Try to load index again, but give up if we are unable to converge. |
Glenn Kasten | f277a7c | 2016-10-14 08:51:20 -0700 | [diff] [blame] | 542 | if (retries-- > 0) { |
| 543 | // bypass the "timeout = NULL;" below |
| 544 | continue; |
| 545 | } |
| 546 | // fall through |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 547 | case EINTR: |
| 548 | case ETIMEDOUT: |
| 549 | err = -errno; |
| 550 | break; |
| 551 | default: |
| 552 | LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno); |
| 553 | break; |
| 554 | } |
| 555 | } |
| 556 | break; |
| 557 | default: |
| 558 | LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync); |
| 559 | break; |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 560 | } |
| 561 | timeout = NULL; |
| 562 | } |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 563 | size_t ourLost; |
| 564 | if (lost == NULL) { |
| 565 | lost = &ourLost; |
| 566 | } |
Glenn Kasten | 0b2947b | 2016-11-22 14:00:23 -0800 | [diff] [blame] | 567 | // returns -EIO if mIsShutdown |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 568 | int32_t filled = mFifo.diff(rear, mLocalFront, lost); |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 569 | mTotalLost += *lost; |
| 570 | mTotalReleased += *lost; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 571 | if (filled < 0) { |
Glenn Kasten | 3ddacbb | 2016-06-20 14:41:03 -0700 | [diff] [blame] | 572 | if (filled == -EOVERFLOW) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 573 | mLocalFront = rear; |
| 574 | } |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 575 | // on error, return an empty slice |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 576 | err = filled; |
| 577 | filled = 0; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 578 | } |
| 579 | size_t availToRead = (size_t) filled; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 580 | if (availToRead > count) { |
| 581 | availToRead = count; |
| 582 | } |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 583 | uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1); |
| 584 | size_t part1 = mFifo.mFrameCount - frontOffset; |
Glenn Kasten | 9b4c805 | 2015-01-06 14:13:13 -0800 | [diff] [blame] | 585 | if (part1 > availToRead) { |
| 586 | part1 = availToRead; |
| 587 | } |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 588 | size_t part2 = part1 > 0 ? availToRead - part1 : 0; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 589 | // return slice |
| 590 | if (iovec != NULL) { |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 591 | iovec[0].mOffset = frontOffset; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 592 | iovec[0].mLength = part1; |
| 593 | iovec[1].mOffset = 0; |
| 594 | iovec[1].mLength = part2; |
| 595 | mObtained = availToRead; |
| 596 | } |
| 597 | return availToRead > 0 ? availToRead : err; |
| 598 | } |
| 599 | |
| 600 | ssize_t audio_utils_fifo_reader::available() |
| 601 | { |
| 602 | return available(NULL /*lost*/); |
| 603 | } |
| 604 | |
| 605 | ssize_t audio_utils_fifo_reader::available(size_t *lost) |
| 606 | { |
Glenn Kasten | 9ddeb2c | 2016-10-14 08:49:02 -0700 | [diff] [blame] | 607 | // iovec == NULL is not part of the public API, but internally it means don't set mObtained |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 608 | return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost); |
| 609 | } |
| 610 | |
Glenn Kasten | 0f85039 | 2016-10-19 12:14:46 -0700 | [diff] [blame] | 611 | ssize_t audio_utils_fifo_reader::flush(size_t *lost) |
| 612 | { |
| 613 | audio_utils_iovec iovec[2]; |
| 614 | ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost); |
| 615 | if (ret > 0) { |
| 616 | size_t flushed = (size_t) ret; |
| 617 | release(flushed); |
| 618 | mTotalFlushed += flushed; |
| 619 | ret = flushed; |
| 620 | } |
| 621 | return ret; |
| 622 | } |
| 623 | |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 624 | void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel) |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 625 | { |
| 626 | // cap to range [0, mFifo.mFrameCount] |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 627 | if (armLevel < 0) { |
| 628 | armLevel = -1; |
| 629 | } else if ((uint32_t) armLevel > mFifo.mFrameCount) { |
| 630 | armLevel = mFifo.mFrameCount; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 631 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 632 | if (triggerLevel > mFifo.mFrameCount) { |
| 633 | triggerLevel = mFifo.mFrameCount; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 634 | } |
| 635 | // TODO this is overly conservative; it would be better to arm based on actual fill level |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 636 | if (armLevel < mArmLevel) { |
| 637 | mIsArmed = true; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 638 | } |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 639 | mArmLevel = armLevel; |
| 640 | mTriggerLevel = triggerLevel; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 641 | } |
| 642 | |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 643 | void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 644 | { |
Glenn Kasten | b9652ab | 2016-11-18 13:34:01 -0800 | [diff] [blame] | 645 | *armLevel = mArmLevel; |
| 646 | *triggerLevel = mTriggerLevel; |
Glenn Kasten | 547a992 | 2016-06-15 13:07:31 -0700 | [diff] [blame] | 647 | } |