fifo.cpp revision dc1ff1f1ee8bdd6baccd558bd4e279ffbbe01400
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 20#include <errno.h> 21#include <limits.h> 22#include <stdlib.h> 23#include <string.h> 24 25// FIXME futex portion is not supported on Mac, should use the Mac alternative 26#ifdef __linux__ 27#include <linux/futex.h> 28#include <sys/syscall.h> 29#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 35 36#include <audio_utils/fifo.h> 37#include <audio_utils/roundup.h> 38#include <cutils/log.h> 39#include <utils/Errors.h> 40 41static int sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) 42{ 43#ifdef __linux__ 44 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); 45#else 46 (void) addr1; 47 (void) op; 48 (void) val1; 49 (void) timeout; 50 (void) addr2; 51 (void) val3; 52 errno = ENOSYS; 53 return -1; 54#endif 55} 56 57audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount, 58 audio_utils_fifo_index& sharedRear, audio_utils_fifo_index *throttleFront) 59 __attribute__((no_sanitize("integer"))) : 60 mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)), 61 mFudgeFactor(mFrameCountP2 - mFrameCount), 62 mIsPrivate(true), 63 mSharedRear(sharedRear), mThrottleFront(throttleFront) 64{ 65 // actual upper bound on frameCount will depend on the frame size 66 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT_MAX)); 67} 68 69audio_utils_fifo_base::~audio_utils_fifo_base() 70{ 71} 72 73uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) 74 __attribute__((no_sanitize("integer"))) 75{ 76 if (mFudgeFactor) { 77 uint32_t mask = mFrameCountP2 - 1; 78 ALOG_ASSERT((index & mask) < mFrameCount); 79 ALOG_ASSERT(increment <= mFrameCountP2); 80 if ((index & mask) + increment >= mFrameCount) { 81 increment += mFudgeFactor; 82 } 83 index += increment; 84 ALOG_ASSERT((index & mask) < mFrameCount); 85 return index; 86 } else { 87 return index + increment; 88 } 89} 90 91int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) 92 __attribute__((no_sanitize("integer"))) 93{ 94 uint32_t diff = rear - front; 95 if (mFudgeFactor) { 96 uint32_t mask = mFrameCountP2 - 1; 97 uint32_t rearMasked = rear & mask; 98 uint32_t frontMasked = front & mask; 99 if (rearMasked >= mFrameCount || frontMasked >= mFrameCount) { 100 return -EIO; 101 } 102 uint32_t genDiff = (rear & ~mask) - (front & ~mask); 103 if (genDiff != 0) { 104 if (genDiff > mFrameCountP2) { 105 if (lost != NULL) { 106 // TODO provide a more accurate estimate 107 *lost = (genDiff / mFrameCountP2) * mFrameCount; 108 } 109 return -EOVERFLOW; 110 } 111 diff -= mFudgeFactor; 112 } 113 } 114 // FIFO should not be overfull 115 if (diff > mFrameCount) { 116 if (lost != NULL) { 117 *lost = diff - mFrameCount; 118 } 119 return -EOVERFLOW; 120 } 121 return (int32_t) diff; 122} 123 124//////////////////////////////////////////////////////////////////////////////// 125 126audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer, 127 audio_utils_fifo_index& sharedRear, audio_utils_fifo_index *throttleFront) 128 __attribute__((no_sanitize("integer"))) : 129 audio_utils_fifo_base(frameCount, sharedRear, throttleFront), 130 mFrameSize(frameSize), mBuffer(buffer) 131{ 132 // maximum value of frameCount * frameSize is INT_MAX (2^31 - 1), not 2^31, because we need to 133 // be able to distinguish successful and error return values from read and write. 134 LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL || 135 frameCount > ((uint32_t) INT_MAX) / frameSize); 136} 137 138audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer, 139 bool throttlesWriter) : 140 audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear, 141 throttlesWriter ? &mSingleProcessSharedFront : NULL) 142{ 143} 144 145audio_utils_fifo::~audio_utils_fifo() 146{ 147} 148 149//////////////////////////////////////////////////////////////////////////////// 150 151audio_utils_fifo_provider::audio_utils_fifo_provider() : 152 mObtained(0) 153{ 154} 155 156audio_utils_fifo_provider::~audio_utils_fifo_provider() 157{ 158} 159 160//////////////////////////////////////////////////////////////////////////////// 161 162audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) : 163 audio_utils_fifo_provider(), mFifo(fifo), mLocalRear(0), 164 mLowLevelArm(fifo.mFrameCount), mHighLevelTrigger(0), mArmed(false), 165 mEffectiveFrames(fifo.mFrameCount) 166{ 167} 168 169audio_utils_fifo_writer::~audio_utils_fifo_writer() 170{ 171} 172 173ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count, struct timespec *timeout) 174 __attribute__((no_sanitize("integer"))) 175{ 176 audio_utils_iovec iovec[2]; 177 ssize_t availToWrite = obtain(iovec, count, timeout); 178 if (availToWrite > 0) { 179 memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer, 180 iovec[0].mLength * mFifo.mFrameSize); 181 if (iovec[1].mLength > 0) { 182 memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize, 183 (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize), 184 iovec[1].mLength * mFifo.mFrameSize); 185 } 186 release(availToWrite); 187 } 188 return availToWrite; 189} 190 191ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count, 192 struct timespec *timeout) 193 __attribute__((no_sanitize("integer"))) 194{ 195 size_t availToWrite; 196 if (mFifo.mThrottleFront != NULL) { 197 uint32_t front; 198 for (;;) { 199 front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, 200 std::memory_order_acquire); 201 int32_t filled = mFifo.diff(mLocalRear, front, NULL /*lost*/); 202 if (filled < 0) { 203 mObtained = 0; 204 return (ssize_t) filled; 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 } 213 int err = sys_futex(&mFifo.mThrottleFront->mIndex, 214 mFifo.mIsPrivate ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, front, timeout, NULL, 0); 215 if (err < 0) { 216 switch (errno) { 217 case EWOULDBLOCK: 218 case EINTR: 219 case ETIMEDOUT: 220 break; 221 default: 222 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno); 223 break; 224 } 225 } 226 timeout = NULL; 227 } 228 } else { 229 availToWrite = mEffectiveFrames; 230 } 231 if (availToWrite > count) { 232 availToWrite = count; 233 } 234 uint32_t rearMasked = mLocalRear & (mFifo.mFrameCountP2 - 1); 235 size_t part1 = mFifo.mFrameCount - rearMasked; 236 if (part1 > availToWrite) { 237 part1 = availToWrite; 238 } 239 size_t part2 = part1 > 0 ? availToWrite - part1 : 0; 240 iovec[0].mOffset = rearMasked; 241 iovec[0].mLength = part1; 242 iovec[1].mOffset = 0; 243 iovec[1].mLength = part2; 244 mObtained = availToWrite; 245 return availToWrite; 246} 247 248void audio_utils_fifo_writer::release(size_t count) 249 __attribute__((no_sanitize("integer"))) 250{ 251 if (count > 0) { 252 LOG_ALWAYS_FATAL_IF(count > mObtained); 253 if (mFifo.mThrottleFront != NULL) { 254 uint32_t front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, 255 std::memory_order_acquire); 256 int32_t filled = mFifo.diff(mLocalRear, front, NULL /*lost*/); 257 mLocalRear = mFifo.sum(mLocalRear, count); 258 atomic_store_explicit(&mFifo.mSharedRear.mIndex, mLocalRear, 259 std::memory_order_release); 260 if (filled >= 0) { 261 if (filled + count <= mLowLevelArm) { 262 mArmed = true; 263 } 264 if (mArmed && filled + count >= mHighLevelTrigger) { 265 int err = sys_futex(&mFifo.mSharedRear.mIndex, 266 mFifo.mIsPrivate ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 267 INT_MAX /*waiters*/, NULL, NULL, 0); 268 // err is number of processes woken up 269 if (err < 0) { 270 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", __func__, err, errno); 271 } 272 mArmed = false; 273 } 274 } 275 } else { 276 mLocalRear = mFifo.sum(mLocalRear, count); 277 atomic_store_explicit(&mFifo.mSharedRear.mIndex, mLocalRear, 278 std::memory_order_release); 279 } 280 mObtained -= count; 281 } 282} 283 284//////////////////////////////////////////////////////////////////////////////// 285 286audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) : 287 audio_utils_fifo_provider(), mFifo(fifo), mLocalFront(0), 288 mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL), 289 mHighLevelArm(0), mLowLevelTrigger(mFifo.mFrameCount), mArmed(false) 290{ 291} 292 293audio_utils_fifo_reader::~audio_utils_fifo_reader() 294{ 295 // TODO Need a way to pass throttle capability to the another reader, should one reader exit. 296} 297 298ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, struct timespec *timeout, 299 size_t *lost) 300 __attribute__((no_sanitize("integer"))) 301{ 302 audio_utils_iovec iovec[2]; 303 ssize_t availToRead = obtain(iovec, count, timeout, lost); 304 if (availToRead > 0) { 305 memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, 306 iovec[0].mLength * mFifo.mFrameSize); 307 if (iovec[1].mLength > 0) { 308 memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize), 309 (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize, 310 iovec[1].mLength * mFifo.mFrameSize); 311 } 312 release(availToRead); 313 } 314 return availToRead; 315} 316 317ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count, 318 struct timespec *timeout) 319 __attribute__((no_sanitize("integer"))) 320{ 321 return obtain(iovec, count, timeout, NULL); 322} 323 324void audio_utils_fifo_reader::release(size_t count) 325 __attribute__((no_sanitize("integer"))) 326{ 327 if (count > 0) { 328 LOG_ALWAYS_FATAL_IF(count > mObtained); 329 if (mThrottleFront != NULL) { 330 uint32_t rear = atomic_load_explicit(&mFifo.mSharedRear.mIndex, 331 std::memory_order_acquire); 332 int32_t filled = mFifo.diff(rear, mLocalFront, NULL /*lost*/); 333 mLocalFront = mFifo.sum(mLocalFront, count); 334 atomic_store_explicit(&mThrottleFront->mIndex, mLocalFront, 335 std::memory_order_release); 336 if (filled >= 0) { 337 if (filled - count >= mHighLevelArm) { 338 mArmed = true; 339 } 340 if (mArmed && filled - count <= mLowLevelTrigger) { 341 int err = sys_futex(&mFifo.mSharedRear.mIndex, 342 mFifo.mIsPrivate ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 343 1 /*waiters*/, NULL, NULL, 0); 344 // err is number of processes woken up 345 if (err < 0 || err > 1) { 346 LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d", __func__, err, errno); 347 } 348 mArmed = false; 349 } 350 } 351 } else { 352 mLocalFront = mFifo.sum(mLocalFront, count); 353 } 354 mObtained -= count; 355 } 356} 357 358ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count, 359 struct timespec *timeout, size_t *lost) 360 __attribute__((no_sanitize("integer"))) 361{ 362 uint32_t rear; 363 for (;;) { 364 rear = atomic_load_explicit(&mFifo.mSharedRear.mIndex, 365 std::memory_order_acquire); 366 // TODO pull out "count == 0" 367 if (count == 0 || rear != mLocalFront || timeout == NULL || 368 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) { 369 break; 370 } 371 int err = sys_futex(&mFifo.mSharedRear.mIndex, 372 mFifo.mIsPrivate ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 373 rear, timeout, NULL, 0); 374 if (err < 0) { 375 switch (errno) { 376 case EWOULDBLOCK: 377 case EINTR: 378 case ETIMEDOUT: 379 break; 380 default: 381 LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno); 382 break; 383 } 384 } 385 timeout = NULL; 386 } 387 int32_t filled = mFifo.diff(rear, mLocalFront, lost); 388 if (filled < 0) { 389 if (filled == -EOVERFLOW) { 390 mLocalFront = rear; 391 } 392 mObtained = 0; 393 return (ssize_t) filled; 394 } 395 size_t availToRead = (size_t) filled; 396 if (availToRead > count) { 397 availToRead = count; 398 } 399 uint32_t frontMasked = mLocalFront & (mFifo.mFrameCountP2 - 1); 400 size_t part1 = mFifo.mFrameCount - frontMasked; 401 if (part1 > availToRead) { 402 part1 = availToRead; 403 } 404 size_t part2 = part1 > 0 ? availToRead - part1 : 0; 405 iovec[0].mOffset = frontMasked; 406 iovec[0].mLength = part1; 407 iovec[1].mOffset = 0; 408 iovec[1].mLength = part2; 409 mObtained = availToRead; 410 return availToRead; 411} 412