2 * Copyright 2014 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "folly/detail/Futex.h"
18 #include "folly/test/DeterministicSchedule.h"
23 #include <gflags/gflags.h>
24 #include <gtest/gtest.h>
25 #include <common/logging/logging.h>
28 using namespace folly::detail;
29 using namespace folly::test;
30 using namespace std::chrono;
32 typedef DeterministicSchedule DSched;
34 template <template<typename> class Atom>
35 void run_basic_tests() {
38 EXPECT_FALSE(f.futexWait(1));
39 EXPECT_EQ(f.futexWake(), 0);
41 auto thr = DSched::thread([&]{
42 EXPECT_TRUE(f.futexWait(0));
45 while (f.futexWake() != 1) {
46 std::this_thread::yield();
52 template<template<typename> class Atom>
53 void run_wait_until_tests();
55 template <typename Clock>
56 void stdAtomicWaitUntilTests() {
57 Futex<std::atomic> f(0);
59 auto thrA = DSched::thread([&]{
61 typename Clock::time_point nowPlus2s = Clock::now() + seconds(2);
62 auto res = f.futexWaitUntil(0, nowPlus2s);
63 EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::AWOKEN);
64 if (res == FutexResult::AWOKEN) {
70 while (f.futexWake() != 1) {
71 std::this_thread::yield();
76 auto start = Clock::now();
77 EXPECT_EQ(f.futexWaitUntil(0, start + milliseconds(100)),
78 FutexResult::TIMEDOUT);
79 LOG(INFO) << "Futex wait timed out after waiting for "
80 << duration_cast<milliseconds>(Clock::now() - start).count()
84 template <typename Clock>
85 void deterministicAtomicWaitUntilTests() {
86 Futex<DeterministicAtomic> f(0);
88 // Futex wait must eventually fail with either FutexResult::TIMEDOUT or
89 // FutexResult::INTERRUPTED
90 auto res = f.futexWaitUntil(0, Clock::now() + milliseconds(100));
91 EXPECT_TRUE(res == FutexResult::TIMEDOUT || res == FutexResult::INTERRUPTED);
95 void run_wait_until_tests<std::atomic>() {
96 stdAtomicWaitUntilTests<system_clock>();
97 stdAtomicWaitUntilTests<steady_clock>();
101 void run_wait_until_tests<DeterministicAtomic>() {
102 deterministicAtomicWaitUntilTests<system_clock>();
103 deterministicAtomicWaitUntilTests<steady_clock>();
106 uint64_t diff(uint64_t a, uint64_t b) {
107 return a > b ? a - b : b - a;
110 void run_system_clock_test() {
111 /* Test to verify that system_clock uses clock_gettime(CLOCK_REALTIME, ...)
112 * for the time_points */
114 const int maxIters = 1000;
116 uint64_t delta = 10000000 /* 10 ms */;
118 /** The following loop is only to make the test more robust in the presence of
119 * clock adjustments that can occur. We just run the loop maxIter times and
120 * expect with very high probability that there will be atleast one iteration
121 * of the test during which clock adjustments > delta have not occurred. */
122 while (iter < maxIters) {
123 uint64_t a = duration_cast<nanoseconds>(system_clock::now()
124 .time_since_epoch()).count();
126 clock_gettime(CLOCK_REALTIME, &ts);
127 uint64_t b = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
129 uint64_t c = duration_cast<nanoseconds>(system_clock::now()
130 .time_since_epoch()).count();
132 if (diff(a, b) <= delta &&
133 diff(b, c) <= delta &&
134 diff(a, c) <= 2 * delta) {
135 /* Success! system_clock uses CLOCK_REALTIME for time_points */
140 EXPECT_TRUE(iter < maxIters);
143 void run_steady_clock_test() {
144 /* Test to verify that steady_clock uses clock_gettime(CLOCK_MONOTONIC, ...)
145 * for the time_points */
146 EXPECT_TRUE(steady_clock::is_steady);
148 uint64_t A = duration_cast<nanoseconds>(steady_clock::now()
149 .time_since_epoch()).count();
152 clock_gettime(CLOCK_MONOTONIC, &ts);
153 uint64_t B = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
155 uint64_t C = duration_cast<nanoseconds>(steady_clock::now()
156 .time_since_epoch()).count();
157 EXPECT_TRUE(A <= B && B <= C);
160 TEST(Futex, clock_source) {
161 run_system_clock_test();
163 /* On some systems steady_clock is just an alias for system_clock. So,
164 * we must skip run_steady_clock_test if the two clocks are the same. */
165 if (!std::is_same<system_clock,steady_clock>::value) {
166 run_steady_clock_test();
170 TEST(Futex, basic_live) {
171 run_basic_tests<std::atomic>();
172 run_wait_until_tests<std::atomic>();
175 TEST(Futex, basic_deterministic) {
176 DSched sched(DSched::uniform(0));
177 run_basic_tests<DeterministicAtomic>();
178 run_wait_until_tests<DeterministicAtomic>();
181 int main(int argc, char ** argv) {
182 testing::InitGoogleTest(&argc, argv);
183 google::ParseCommandLineFlags(&argc, &argv, true);
184 return RUN_ALL_TESTS();