fix timeWaiting computation in TimeUtil
[folly.git] / folly / io / async / test / TimeUtilTest.cpp
diff --git a/folly/io/async/test/TimeUtilTest.cpp b/folly/io/async/test/TimeUtilTest.cpp
new file mode 100644 (file)
index 0000000..2945f1f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <thread>
+
+#include <glog/logging.h>
+
+#include <folly/io/async/test/TimeUtil.h>
+#include <folly/portability/GTest.h>
+#include <folly/portability/Unistd.h>
+
+using folly::TimePoint;
+using namespace std::literals::chrono_literals;
+using std::chrono::duration_cast;
+using std::chrono::milliseconds;
+using std::chrono::nanoseconds;
+using std::chrono::steady_clock;
+
+// Define a PrintTo() function for std::chrono::nanoseconds so that these
+// will be printed nicely on EXPECT*() failures.
+// Define this in std::chrono so that argument-dependent lookup works.
+namespace std {
+namespace chrono {
+static inline void PrintTo(nanoseconds ns, ::std::ostream* os) {
+  *os << ns.count() << "ns";
+}
+}
+}
+
+#ifdef __linux__
+void runThread(nanoseconds duration, nanoseconds* timeWaiting) {
+  TimePoint start;
+
+  // Loop consuming CPU until the duration has expired.
+  while (true) {
+    TimePoint now;
+    if (now.getTimeStart() - start.getTimeStart() > duration) {
+      // Time to quit
+      // Report how long we spent waiting to be scheduled on the CPU.
+      *timeWaiting = (now.getTimeWaiting() - start.getTimeWaiting());
+      VLOG(1) << "thread " << start.getTid() << ": elapsed "
+              << duration_cast<milliseconds>(
+                     now.getTimeStart() - start.getTimeStart())
+                     .count()
+              << "ms, time waiting: "
+              << duration_cast<milliseconds>(*timeWaiting).count() << "ms";
+      break;
+    }
+  }
+}
+
+// Test to make sure that TimePoint computes sane values for time
+// spent waiting on CPU.
+TEST(TimeUtil, getTimeWaiting) {
+  TimePoint tp;
+
+  // Run twice as many threads as CPU cores, to ensure that some of
+  // them should be waiting sometime.
+  auto numThreads = sysconf(_SC_NPROCESSORS_CONF) * 2;
+
+  std::vector<std::thread> threads;
+  std::vector<nanoseconds> timeWaiting;
+  timeWaiting.resize(numThreads, 0ns);
+
+  auto start = steady_clock::now();
+  for (int n = 0; n < numThreads; ++n) {
+    threads.emplace_back(runThread, 1s, &timeWaiting[n]);
+  }
+
+  for (auto& thread : threads) {
+    thread.join();
+  }
+  auto end = steady_clock::now();
+
+  auto timeSpent = end - start;
+  nanoseconds max{0};
+  for (int n = 0; n < numThreads; ++n) {
+    max = std::max(max, timeWaiting[n]);
+    // No thread could possibly have been waiting for longer than
+    // the test actually took to run.
+    EXPECT_LT(timeWaiting[n], timeSpent);
+  }
+  // Make sure that at least one thread spent some time waiting
+  EXPECT_GE(max, 1ns);
+}
+#endif