OSDN Git Service

Handle futex status EWOULDBLOCK
authorGlenn Kasten <gkasten@google.com>
Fri, 14 Oct 2016 15:51:20 +0000 (08:51 -0700)
committerGlenn Kasten <gkasten@google.com>
Tue, 18 Oct 2016 01:15:38 +0000 (18:15 -0700)
Test: very difficult, as it is a race condition that rarely occurs
Change-Id: Ibefe09d1e9d940f57463391ed339325b42b84b73

audio_utils/fifo.cpp
audio_utils/include/audio_utils/fifo.h

index 325445a..b4cb401 100644 (file)
@@ -228,6 +228,7 @@ ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count
     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 @@ ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count
                 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 @@ ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count
         __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 @@ ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count
             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;
index a2d137c..058e286 100644 (file)
@@ -240,6 +240,8 @@ public:
      *                      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 @@ public:
 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 @@ public:
      *                     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 @@ public:
      *                      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);