1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/io/async/test/TimeUtil.h>
18 
19 #include <thread>
20 
21 #include <glog/logging.h>
22 
23 #include <folly/portability/GTest.h>
24 #include <folly/portability/Unistd.h>
25 
26 using folly::TimePoint;
27 using namespace std::literals::chrono_literals;
28 using std::chrono::duration_cast;
29 using std::chrono::milliseconds;
30 using std::chrono::nanoseconds;
31 using std::chrono::steady_clock;
32 
33 // Define a PrintTo() function for std::chrono::nanoseconds so that these
34 // will be printed nicely on EXPECT*() failures.
35 // Define this in std::chrono so that argument-dependent lookup works.
36 namespace std {
37 namespace chrono {
PrintTo(nanoseconds ns,::std::ostream * os)38 static inline void PrintTo(nanoseconds ns, ::std::ostream* os) {
39   *os << ns.count() << "ns";
40 }
41 } // namespace chrono
42 } // namespace std
43 
44 #ifdef __linux__
runThread(nanoseconds duration,nanoseconds * timeWaiting)45 void runThread(nanoseconds duration, nanoseconds* timeWaiting) {
46   TimePoint start;
47 
48   // Loop consuming CPU until the duration has expired.
49   while (true) {
50     TimePoint now;
51     if (now.getTimeStart() - start.getTimeStart() > duration) {
52       // Time to quit
53       // Report how long we spent waiting to be scheduled on the CPU.
54       *timeWaiting = (now.getTimeWaiting() - start.getTimeWaiting());
55       VLOG(1) << "thread " << start.getTid() << ": elapsed "
56               << duration_cast<milliseconds>(
57                      now.getTimeStart() - start.getTimeStart())
58                      .count()
59               << "ms, time waiting: "
60               << duration_cast<milliseconds>(*timeWaiting).count() << "ms";
61       break;
62     }
63   }
64 }
65 
66 // Test to make sure that TimePoint computes sane values for time
67 // spent waiting on CPU.
TEST(TimeUtil,getTimeWaiting)68 TEST(TimeUtil, getTimeWaiting) {
69   TimePoint tp;
70 
71   // Run twice as many threads as CPU cores, to ensure that some of
72   // them should be waiting sometime.
73   auto numThreads = sysconf(_SC_NPROCESSORS_CONF) * 2;
74 
75   std::vector<std::thread> threads;
76   std::vector<nanoseconds> timeWaiting;
77   timeWaiting.resize(numThreads, 0ns);
78 
79   auto start = steady_clock::now();
80   for (int n = 0; n < numThreads; ++n) {
81     threads.emplace_back(runThread, 1s, &timeWaiting[n]);
82   }
83 
84   for (auto& thread : threads) {
85     thread.join();
86   }
87   auto end = steady_clock::now();
88 
89   auto timeSpent = end - start;
90   nanoseconds max{0};
91   for (int n = 0; n < numThreads; ++n) {
92     max = std::max(max, timeWaiting[n]);
93     // No thread could possibly have been waiting for longer than
94     // the test actually took to run.
95     EXPECT_LT(timeWaiting[n], timeSpent);
96   }
97   // Make sure that at least one thread spent some time waiting
98   EXPECT_GE(max, 1ns);
99 }
100 #endif
101