Handle futex status EWOULDBLOCK
Test: very difficult, as it is a race condition that rarely occurs
Change-Id: Ibefe09d1e9d940f57463391ed339325b42b84b73
diff --git a/audio_utils/fifo.cpp b/audio_utils/fifo.cpp
index 325445a..b4cb401 100644
--- a/audio_utils/fifo.cpp
+++ b/audio_utils/fifo.cpp
@@ -228,6 +228,7 @@
int err = 0;
size_t availToWrite;
if (mFifo.mThrottleFront != NULL) {
+ int retries = kRetries;
uint32_t front;
for (;;) {
front = atomic_load_explicit(&mFifo.mThrottleFront->mIndex, std::memory_order_acquire);
@@ -270,6 +271,13 @@
err = sys_futex(&mFifo.mThrottleFront->mIndex, op, front, timeout, NULL, 0);
if (err < 0) {
switch (errno) {
+ case EWOULDBLOCK:
+ // benign race condition with partner, try again
+ if (retries-- > 0) {
+ // bypass the "timeout = NULL;" below
+ continue;
+ }
+ // fall through
case EINTR:
case ETIMEDOUT:
err = -errno;
@@ -506,6 +514,7 @@
__attribute__((no_sanitize("integer")))
{
int err = 0;
+ int retries = kRetries;
uint32_t rear;
for (;;) {
rear = atomic_load_explicit(&mFifo.mWriterRear.mIndex,
@@ -537,6 +546,13 @@
err = sys_futex(&mFifo.mWriterRear.mIndex, op, rear, timeout, NULL, 0);
if (err < 0) {
switch (errno) {
+ case EWOULDBLOCK:
+ // benign race condition with partner, try again
+ if (retries-- > 0) {
+ // bypass the "timeout = NULL;" below
+ continue;
+ }
+ // fall through
case EINTR:
case ETIMEDOUT:
err = -errno;
diff --git a/audio_utils/include/audio_utils/fifo.h b/audio_utils/include/audio_utils/fifo.h
index a2d137c..058e286 100644
--- a/audio_utils/include/audio_utils/fifo.h
+++ b/audio_utils/include/audio_utils/fifo.h
@@ -240,6 +240,8 @@
* timeout expired, and no frames were available after the timeout.
* \retval -EINTR count is greater than zero, timeout is non-NULL and not {0, 0}, timeout
* was interrupted by a signal, and no frames were available after signal.
+ * \retval -EWOULDBLOCK count is greater than zero, timeout is non-NULL and not {0, 0},
+ * and lost wake-up retries failed. Should usually handle like -EINTR.
*
* Applications should treat all of these as equivalent to zero available frames,
* except they convey extra information as to the cause.
@@ -270,6 +272,9 @@
protected:
/** Number of frames obtained at most recent obtain(), less total number of frames released. */
uint32_t mObtained;
+
+ /** Number of times to retry a futex wait fails with EWOULDBLOCK. */
+ static const int kRetries = 2;
};
////////////////////////////////////////////////////////////////////////////////
@@ -311,6 +316,8 @@
* timeout expired, and no frames were available after the timeout.
* \retval -EINTR count is greater than zero, timeout is non-NULL and not {0, 0}, timeout
* was interrupted by a signal, and no frames were available after signal.
+ * \retval -EWOULDBLOCK count is greater than zero, timeout is non-NULL and not {0, 0},
+ * and lost wake-up retries failed. Should usually handle like -EINTR.
*/
ssize_t write(const void *buffer, size_t count, const struct timespec *timeout = NULL);
@@ -425,6 +432,8 @@
* timeout expired, and no frames were available after the timeout.
* \retval -EINTR count is greater than zero, timeout is non-NULL and not {0, 0}, timeout
* was interrupted by a signal, and no frames were available after signal.
+ * \retval -EWOULDBLOCK count is greater than zero, timeout is non-NULL and not {0, 0},
+ * and lost wake-up retries failed. Should usually handle like -EINTR.
*/
ssize_t read(void *buffer, size_t count, const struct timespec *timeout = NULL,
size_t *lost = NULL);