Os: add Nanosleep()
am: 960c8a88c8

Change-Id: I639d4d32cc5113a7aa504f138425ba1534a225bc
diff --git a/os.cpp b/os.cpp
index ab178c1..b9093e7 100644
--- a/os.cpp
+++ b/os.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-
 #include <algorithm>
+#include <cerrno>
 #include <cstdint>
+#include <cstring>
 
 #include "android-base/logging.h"
 
@@ -70,6 +70,26 @@
   return now_timestamp;
 }
 
+void Os::Nanosleep(uint32_t sleep_time_nsec) {
+  struct timespec sleep_timespec = {
+      0,  // tv_sec
+      SAFELY_CLAMP(sleep_time_nsec, decltype(timespec::tv_nsec), 0, kMaxNanos)};
+
+  int failed = 0;
+  do {
+    struct timespec remaining_timespec;
+    failed = raw_os_->Nanosleep(&sleep_timespec, &remaining_timespec);
+    sleep_timespec = remaining_timespec;
+  } while (failed && errno == EINTR && sleep_timespec.tv_nsec > 0);
+
+  if (failed && errno != EINTR) {
+    // The only other documented errors for the underlying nanosleep() call are
+    // EFAULT and EINVAL. But we always pass valid pointers, and the values in
+    // |sleep_timespec| are always valid.
+    LOG(FATAL) << "Unexpected error: " << std::strerror(errno);
+  }
+}
+
 std::tuple<size_t, Os::Errno> Os::ReceiveDatagram(int fd, void* buf,
                                                   size_t buflen) {
   // recv() takes a size_t, but returns an ssize_t. That means that the largest
diff --git a/os.h b/os.h
index 31fecbe..290f9e9 100644
--- a/os.h
+++ b/os.h
@@ -49,6 +49,7 @@
   };
 
   static constexpr int kInvalidFd = -1;
+  static constexpr auto kMaxNanos = 999'999'999;
 
   // Constructs an Os instance.
   Os();
@@ -68,6 +69,10 @@
   // Returns the current time, as reported by the clock with |clock_id|.
   virtual Timestamp GetTimestamp(clockid_t clock_id) const;
 
+  // Suspends execution of this process, for |sleep_time_nsec|. The passed
+  // value must not exceed kMaxNanos.
+  virtual void Nanosleep(uint32_t sleep_time_nsec);
+
   // Receives a datagram of up to |buflen| from |fd|, writing the data to |buf|.
   // Returns the size of the datagram, and the result of the operation (0 for
   // success, |errno| otherwise).
diff --git a/raw_os.cpp b/raw_os.cpp
index 8d08901..895f2a8 100644
--- a/raw_os.cpp
+++ b/raw_os.cpp
@@ -39,6 +39,10 @@
   return android_get_control_socket(socket_name);
 }
 
+int RawOs::Nanosleep(const struct timespec* req, struct timespec* rem) {
+  return nanosleep(req, rem);
+}
+
 ssize_t RawOs::Recv(int sockfd, void* buf, size_t buflen, int flags) {
   return recv(sockfd, buf, buflen, flags);
 }
diff --git a/raw_os.h b/raw_os.h
index 3ba9a0f..2658fde 100644
--- a/raw_os.h
+++ b/raw_os.h
@@ -41,6 +41,10 @@
   // See android_get_control_socket().
   virtual int GetControlSocket(const char* socket_name);
 
+  // See nanosleep().
+  virtual int Nanosleep(NONNULL const struct timespec* req,
+                        struct timespec* rem);
+
   // See recv().
   virtual ssize_t Recv(int sockfd, void* buf, size_t buflen, int flags);
 
diff --git a/tests/mock_os.h b/tests/mock_os.h
index 3e02730..410b099 100644
--- a/tests/mock_os.h
+++ b/tests/mock_os.h
@@ -36,6 +36,7 @@
   MOCK_CONST_METHOD1(GetTimestamp, Timestamp(clockid_t clock_id));
   MOCK_METHOD1(GetControlSocket,
                std::tuple<int, Errno>(const std::string& socket_name));
+  MOCK_METHOD1(Nanosleep, void(uint32_t sleep_time_nsec));
   MOCK_METHOD3(ReceiveDatagram,
                std::tuple<size_t, Errno>(int fd, void* buf, size_t buflen));
   MOCK_METHOD3(Write, std::tuple<size_t, Os::Errno>(int fd, const void* buf,
diff --git a/tests/mock_raw_os.h b/tests/mock_raw_os.h
index 3931cb0..9661635 100644
--- a/tests/mock_raw_os.h
+++ b/tests/mock_raw_os.h
@@ -33,6 +33,8 @@
   MOCK_CONST_METHOD2(ClockGettime,
                      int(clockid_t clock_id, struct timespec* tspec));
   MOCK_METHOD1(GetControlSocket, int(const char* socket_name));
+  MOCK_METHOD2(Nanosleep,
+               int(const struct timespec* req, struct timespec* rem));
   MOCK_METHOD4(Recv, ssize_t(int sockfd, void* buf, size_t buflen, int flags));
   MOCK_METHOD3(Write, ssize_t(int fd, const void* buf, size_t buflen));
 
diff --git a/tests/os_unittest.cpp b/tests/os_unittest.cpp
index e90755a..0cfb493 100644
--- a/tests/os_unittest.cpp
+++ b/tests/os_unittest.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <array>
+#include <iostream>
 #include <memory>
 #include <tuple>
 
@@ -25,11 +26,25 @@
 #include "wifilogd/os.h"
 #include "wifilogd/tests/mock_raw_os.h"
 
+// This function must be defined in the same namespace as |timespec|. Hence the
+// placement of this function at the top level.
+inline void PrintTo(const timespec& ts, ::std::ostream* os) {
+  *os << "[secs:" << ts.tv_sec << " "
+      << "nsecs:" << ts.tv_nsec << "]";
+}
+
 namespace android {
 namespace wifilogd {
 namespace {
 
 using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InSequence;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+using ::testing::NotNull;
+using ::testing::Pointee;
 using ::testing::Return;
 using ::testing::SetArgumentPointee;
 using ::testing::SetErrnoAndReturn;
@@ -52,6 +67,29 @@
   MockRawOs* raw_os_;
 };
 
+class TimespecMatcher : public MatcherInterface<const timespec&> {
+ public:
+  explicit TimespecMatcher(const timespec& expected) : expected_(expected) {}
+
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "equals ";
+    PrintTo(expected_, os);
+  }
+
+  virtual bool MatchAndExplain(const timespec& actual,
+                               MatchResultListener* /* listener */) const {
+    return actual.tv_sec == expected_.tv_sec &&
+           actual.tv_nsec == expected_.tv_nsec;
+  }
+
+ private:
+  const timespec& expected_;
+};
+
+Matcher<const timespec&> EqualsTimespec(const timespec& expected) {
+  return MakeMatcher(new TimespecMatcher(expected));
+}
+
 }  // namespace
 
 TEST_F(OsTest, GetControlSocketReturnsFdAndZeroOnSuccess) {
@@ -86,6 +124,72 @@
   EXPECT_EQ(kFakeNsecs, received.nsecs);
 }
 
+TEST_F(OsTest, NanosleepPassesNormalValueToSyscall) {
+  constexpr auto kSleepTimeNsec = 100;
+  EXPECT_CALL(*raw_os_,
+              Nanosleep(Pointee(EqualsTimespec({0, kSleepTimeNsec})), _));
+  os_->Nanosleep(kSleepTimeNsec);
+}
+
+TEST_F(OsTest, NanosleepPassesMaxmimalValueToSyscall) {
+  EXPECT_CALL(*raw_os_,
+              Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _));
+  os_->Nanosleep(Os::kMaxNanos);
+}
+
+TEST_F(OsTest, NanosleepPassesZeroValueToSyscall) {
+  EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 0})), _));
+  os_->Nanosleep(0);
+}
+
+TEST_F(OsTest, NanosleepClampsOverlyLargeValue) {
+  EXPECT_CALL(*raw_os_,
+              Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _));
+  os_->Nanosleep(Os::kMaxNanos + 1);
+}
+
+TEST_F(OsTest, NanosleepRetriesOnInterruptedCall) {
+  InSequence seq;
+  EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull()))
+      .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) {
+        *remaining = {0, 100};
+        errno = EINTR;
+        return -1;
+      }));
+  EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 100})), _));
+  os_->Nanosleep(Os::kMaxNanos);
+}
+
+TEST_F(OsTest, NanosleepRetriesMultipleTimesIfNecessary) {
+  InSequence seq;
+  EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull()))
+      .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) {
+        *remaining = {0, 100};
+        errno = EINTR;
+        return -1;
+      }));
+  EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull()))
+      .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) {
+        *remaining = {0, 50};
+        errno = EINTR;
+        return -1;
+      }));
+  EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 50})), _));
+  os_->Nanosleep(Os::kMaxNanos);
+}
+
+TEST_F(OsTest, NanosleepIgnoresEintrWithZeroTimeRemaining) {
+  InSequence seq;
+  EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull()))
+      .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) {
+        *remaining = {0, 0};
+        errno = EINTR;
+        return -1;
+      }));
+  EXPECT_CALL(*raw_os_, Nanosleep(_, _)).Times(0);
+  os_->Nanosleep(Os::kMaxNanos);
+}
+
 TEST_F(OsTest, ReceiveDatagramReturnsCorrectValueForMaxSizedDatagram) {
   constexpr int kFakeFd = 100;
   std::array<uint8_t, 8192> buffer{};
@@ -217,6 +321,12 @@
   EXPECT_DEATH(os_->GetTimestamp(CLOCK_REALTIME), "Unexpected error");
 }
 
+TEST_F(OsDeathTest, NanosleepUnexpectedErrorCausesDeath) {
+  ON_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _))
+      .WillByDefault(SetErrnoAndReturn(EFAULT, -1));
+  EXPECT_DEATH(os_->Nanosleep(Os::kMaxNanos), "Unexpected error");
+}
+
 TEST_F(OsDeathTest, ReceiveDatagramWithOverlyLargeBufferCausesDeath) {
   constexpr int kFakeFd = 100;
   std::array<uint8_t, 8192> buffer{};