summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormukesh agrawal <quiche@google.com>2016-10-27 17:39:18 -0700
committermukesh agrawal <quiche@google.com>2016-11-15 11:12:35 -0800
commit960c8a88c83c55c21a3286628591a41d2fb42231 (patch)
treeafe37b9a1fe111663b225442fb6514ca5c11f7d4
parent3b1d20a5720a5fe6b0a1eccd577b0d2447139efc (diff)
downloadwifilogd-960c8a88c83c55c21a3286628591a41d2fb42231.tar.gz
Os: add Nanosleep()
Add Nanosleep(), so that upper layers of wifilogd can briefly suspend execution after a transient error. Bug: 32454491 Test: ./runtest.sh (on angler) Change-Id: I85e95ddf4139ae10ab95a40c46ed2a424e2a28ad
-rw-r--r--os.cpp24
-rw-r--r--os.h5
-rw-r--r--raw_os.cpp4
-rw-r--r--raw_os.h4
-rw-r--r--tests/mock_os.h1
-rw-r--r--tests/mock_raw_os.h2
-rw-r--r--tests/os_unittest.cpp110
7 files changed, 148 insertions, 2 deletions
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 @@ Os::Timestamp Os::GetTimestamp(clockid_t clock_id) const {
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 @@ class Os {
};
static constexpr int kInvalidFd = -1;
+ static constexpr auto kMaxNanos = 999'999'999;
// Constructs an Os instance.
Os();
@@ -68,6 +69,10 @@ class Os {
// 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 @@ int RawOs::GetControlSocket(const char* socket_name) {
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 @@ class RawOs {
// 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 @@ class MockOs : public Os {
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 @@ class MockRawOs : public RawOs {
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 @@ class OsTest : public ::testing::Test {
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 @@ TEST_F(OsTest, GetTimestampSucceeds) {
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 @@ TEST_F(OsDeathTest, GetTimestampRawOsErrorCausesDeath) {
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{};