Add optional retry parameter to EventFlag wait()
am: f1aa0581d5

Change-Id: Ib1deb74426212e02773ccf32e3826a5fec870882
diff --git a/EventFlag.cpp b/EventFlag.cpp
index e391ef2..4d370b0 100644
--- a/EventFlag.cpp
+++ b/EventFlag.cpp
@@ -21,6 +21,7 @@
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 #include <new>
 
 namespace android {
@@ -130,7 +131,7 @@
  * Wait for any of the bits in the bitmask to be set
  * and return which bits caused the return.
  */
-status_t EventFlag::wait(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
+status_t EventFlag::waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeoutNanoSeconds) {
     /*
      * Return early if there are no set bits in bitmask.
      */
@@ -185,6 +186,46 @@
     return status;
 }
 
+/*
+ * Wait for any of the bits in the bitmask to be set
+ * and return which bits caused the return. If 'retry'
+ * is true, wait again on a spurious wake-up.
+ */
+status_t EventFlag::wait(uint32_t bitmask,
+                         uint32_t* efState,
+                         int64_t timeoutNanoSeconds,
+                         bool retry) {
+    if (!retry) {
+        return waitHelper(bitmask, efState, timeoutNanoSeconds);
+    }
+
+    bool shouldTimeOut = timeoutNanoSeconds != 0;
+    int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
+    status_t status;
+    while (true) {
+        if (shouldTimeOut) {
+            int64_t currentTimeNs = android::elapsedRealtimeNano();
+            /*
+             * Decrement TimeOutNanos to account for the time taken to complete the last
+             * iteration of the while loop.
+             */
+            timeoutNanoSeconds -= currentTimeNs - prevTimeNs;
+            prevTimeNs = currentTimeNs;
+            if (timeoutNanoSeconds <= 0) {
+                status = -ETIMEDOUT;
+                *efState = 0;
+                break;
+            }
+        }
+
+        status = waitHelper(bitmask, efState, timeoutNanoSeconds);
+        if ((status != -EAGAIN) && (status != -EINTR)) {
+            break;
+        }
+    }
+    return status;
+}
+
 status_t EventFlag::unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
                                        bool* efWordNeedsUnmapping) {
     status_t status = NO_ERROR;
diff --git a/include/fmq/EventFlag.h b/include/fmq/EventFlag.h
index f452e10..af18448 100644
--- a/include/fmq/EventFlag.h
+++ b/include/fmq/EventFlag.h
@@ -92,15 +92,20 @@
      * @param timeoutNanoSeconds Specifies timeout duration in nanoseconds. It is converted to
      * an absolute timeout for the wait according to the CLOCK_MONOTONIC clock.
      * @param efState The event flag bits that caused the return from wake.
+     * @param retry If true, retry automatically for a spurious wake. If false,
+     * will return -EINTR or -EAGAIN for a spurious wake.
      *
      * @return Returns a status_t error code. Likely error codes are
      * NO_ERROR if the method is successful, BAD_VALUE due to bad input
      * parameters, TIMED_OUT if the wait timedout as per the timeout
      * parameter, -EAGAIN or -EINTR to indicate that the caller needs to invoke
-     * wait() again.
+     * wait() again. -EAGAIN or -EINTR error codes will not be returned if
+     * 'retry' is true since the method will retry waiting in that case.
      */
-    status_t wait(uint32_t bitmask, uint32_t* efState, int64_t timeOutNanoSeconds = 0);
-
+    status_t wait(uint32_t bitmask,
+                  uint32_t* efState,
+                  int64_t timeOutNanoSeconds = 0,
+                  bool retry = false);
 private:
     bool mEfWordNeedsUnmapping = false;
     std::atomic<uint32_t>* mEfWordPtr = nullptr;
@@ -124,12 +129,19 @@
     EventFlag(const EventFlag& other) = delete;
 
     /*
+     * Wait for any of the bits in the bit mask to be set.
+     */
+    status_t waitHelper(uint32_t bitmask, uint32_t* efState, int64_t timeOutNanoSeconds);
+
+    /*
      * Utility method to unmap the event flag word.
      */
     static status_t unmapEventFlagWord(std::atomic<uint32_t>* efWordPtr,
                                        bool* efWordNeedsUnmapping);
+
     /*
-     * Utility method to convert timeout duration to an absolute value.
+     * Utility method to convert timeout duration to an absolute CLOCK_MONOTONIC
+     * clock time which is required by futex syscalls.
      */
     inline void addNanosecondsToCurrentTime(int64_t nanoseconds, struct timespec* timeAbs);
     ~EventFlag();
diff --git a/include/fmq/MessageQueue.h b/include/fmq/MessageQueue.h
index 5045787..b8a4c2f 100644
--- a/include/fmq/MessageQueue.h
+++ b/include/fmq/MessageQueue.h
@@ -761,19 +761,23 @@
     }
 
     bool shouldTimeOut = timeOutNanos != 0;
-    int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
+    int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
 
-    bool endWait = false;
-    while (endWait == false) {
-        /* It is not require to adjust 'timeOutNanos' if 'shouldTimeOut' is true */
+    while (true) {
+        /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
         if (shouldTimeOut) {
+            /*
+             * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
+             * to Nanoseconds)
+             */
             int64_t currentTimeNs = android::elapsedRealtimeNano();
             /*
-             * Decrement TimeOutNanos to account for the time taken to complete the last
+             * Decrement 'timeOutNanos' to account for the time taken to complete the last
              * iteration of the while loop.
              */
-            timeOutNanos -= currentTimeNs - prevTimeNs;
-            prevTimeNs = currentTimeNs;
+            timeOutNanos -= currentTimeNs - prevTimeNanos;
+            prevTimeNanos = currentTimeNs;
+
             if (timeOutNanos <= 0) {
                 /*
                  * Attempt write in case a context switch happened outside of
@@ -789,44 +793,27 @@
          * notification.
          */
         uint32_t efState = 0;
-        status_t status = evFlag->wait(readNotification, &efState, timeOutNanos);
-        switch (status) {
-            case android::NO_ERROR:
-                /*
-                 * If wait() returns NO_ERROR, break and check efState.
-                 */
-                break;
-            case android::TIMED_OUT:
-                /*
-                 * If wait() returns android::TIMEDOUT, break out of the while loop
-                 * and return false;
-                 */
-                endWait = true;
-                continue;
-            case -EAGAIN:
-            case -EINTR:
-                /*
-                 * For errors -EAGAIN and -EINTR, go back to wait.
-                 */
-                continue;
-            default:
-                /*
-                 * Throw an error for any other error code since it is unexpected.
-                 */
+        status_t status = evFlag->wait(readNotification,
+                                       &efState,
+                                       timeOutNanos,
+                                       true /* retry on spurious wake */);
 
-                endWait = true;
-                ALOGE("Unexpected error code from EventFlag Wait %d", status);
-                continue;
+        if (status != android::TIMED_OUT && status != android::NO_ERROR) {
+            ALOGE("Unexpected error code from EventFlag Wait write %d", status);
+            break;
+        }
+
+        if (status == android::TIMED_OUT) {
+            break;
         }
 
         /*
-         * If the wake() was not due to the readNotification bit or if
-         * there is still insufficient space to write to the FMQ,
+         * If there is still insufficient space to write to the FMQ,
          * keep waiting for another readNotification.
          */
         if ((efState & readNotification) && write(data, count)) {
             result = true;
-            endWait = true;
+            break;
         }
     }
 
@@ -889,19 +876,22 @@
     }
 
     bool shouldTimeOut = timeOutNanos != 0;
-    int64_t prevTimeNs = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
+    int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;
 
-    bool endWait = false;
-    while (endWait == false) {
-        /* It is not require to adjust 'timeOutNanos' if 'shouldTimeOut' is true */
+    while (true) {
+        /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
         if (shouldTimeOut) {
+            /*
+             * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
+             * to Nanoseconds)
+             */
             int64_t currentTimeNs = android::elapsedRealtimeNano();
             /*
-             * Decrement TimeOutNanos to account for the time taken to complete the last
+             * Decrement 'timeOutNanos' to account for the time taken to complete the last
              * iteration of the while loop.
              */
-            timeOutNanos -= currentTimeNs - prevTimeNs;
-            prevTimeNs = currentTimeNs;
+            timeOutNanos -= currentTimeNs - prevTimeNanos;
+            prevTimeNanos = currentTimeNs;
 
             if (timeOutNanos <= 0) {
                 /*
@@ -918,44 +908,27 @@
          * notification.
          */
         uint32_t efState = 0;
-        status_t status = evFlag->wait(writeNotification, &efState, timeOutNanos);
-        switch (status) {
-            case android::NO_ERROR:
-                /*
-                 * If wait() returns NO_ERROR, break and check efState.
-                 */
-                break;
-            case android::TIMED_OUT:
-                /*
-                 * If wait() returns android::TIMEDOUT, break out of the while loop
-                 * and return false;
-                 */
-                endWait = true;
-                continue;
-            case -EAGAIN:
-            case -EINTR:
-                /*
-                 * For errors -EAGAIN and -EINTR, go back to wait.
-                 */
-                continue;
-            default:
-                /*
-                 * Throw an error for any other error code since it is unexpected.
-                 */
+        status_t status = evFlag->wait(writeNotification,
+                                       &efState,
+                                       timeOutNanos,
+                                       true /* retry on spurious wake */);
 
-                endWait = true;
-                ALOGE("Unexpected error code from EventFlag Wait %d", status);
-                continue;
+        if (status != android::TIMED_OUT && status != android::NO_ERROR) {
+            ALOGE("Unexpected error code from EventFlag Wait status %d", status);
+            break;
+        }
+
+        if (status == android::TIMED_OUT) {
+            break;
         }
 
         /*
-         * If the wake() was not due to the writeNotification bit being set
-         * or if the data in FMQ is still insufficient, go back to waiting
+         * If the data in FMQ is still insufficient, go back to waiting
          * for another write notification.
          */
         if ((efState & writeNotification) && read(data, count)) {
             result = true;
-            endWait = true;
+            break;
         }
     }