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