2 * Copyright 2015 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/futures/Barrier.h>
20 #include <condition_variable>
23 #include <folly/Random.h>
24 #include <glog/logging.h>
25 #include <gtest/gtest.h>
27 DEFINE_int32(seed, 0, "Random seed");
29 namespace folly { namespace futures { namespace test {
31 TEST(BarrierTest, Simple) {
32 constexpr uint32_t numThreads = 10;
35 std::condition_variable b1DoneCond;
36 std::condition_variable b2DoneCond;
37 std::atomic<uint32_t> b1TrueSeen(0);
38 std::atomic<uint32_t> b1Passed(0);
39 std::atomic<uint32_t> b2TrueSeen(0);
40 std::atomic<uint32_t> b2Passed(0);
42 Barrier barrier(numThreads + 1);
44 std::vector<std::thread> threads;
45 threads.reserve(numThreads);
46 for (uint32_t i = 0; i < numThreads; ++i) {
47 threads.emplace_back([&] () {
51 std::unique_lock<std::mutex> lock(mutex);
52 b1TrueSeen += uint32_t(v);
53 if (++b1Passed == numThreads) {
54 b1DoneCond.notify_one();
56 return barrier.wait();
60 std::unique_lock<std::mutex> lock(mutex);
61 b2TrueSeen += uint32_t(v);
62 if (++b2Passed == numThreads) {
63 b2DoneCond.notify_one();
71 std::this_thread::sleep_for(std::chrono::milliseconds(50));
72 EXPECT_EQ(0, b1Passed);
73 EXPECT_EQ(0, b1TrueSeen);
75 b1TrueSeen += barrier.wait().get();
78 std::unique_lock<std::mutex> lock(mutex);
79 while (b1Passed != numThreads) {
80 b1DoneCond.wait(lock);
82 EXPECT_EQ(1, b1TrueSeen);
86 std::this_thread::sleep_for(std::chrono::milliseconds(50));
87 EXPECT_EQ(0, b2Passed);
88 EXPECT_EQ(0, b2TrueSeen);
90 b2TrueSeen += barrier.wait().get();
93 std::unique_lock<std::mutex> lock(mutex);
94 while (b2Passed != numThreads) {
95 b2DoneCond.wait(lock);
97 EXPECT_EQ(1, b2TrueSeen);
100 for (auto& t : threads) {
105 TEST(BarrierTest, Random) {
106 // Create numThreads threads.
108 // Each thread repeats the following numIterations times:
109 // - grab a randomly chosen number of futures from the barrier, waiting
110 // for a short random time between each
111 // - wait for all futures to complete
112 // - record whether the one future returning true was seen among them
114 // At the end, we verify that exactly one future returning true was seen
115 // for each iteration.
116 constexpr uint32_t numIterations = 1;
117 auto numThreads = folly::Random::rand32(30, 91);
122 uint32_t iteration = 0;
124 std::vector<uint32_t> trueSeen;
127 std::vector<ThreadInfo> threads;
128 threads.resize(numThreads);
130 uint32_t totalFutures = 0;
131 for (auto& tinfo : threads) {
132 tinfo.numFutures = folly::Random::rand32(100);
133 tinfo.trueSeen.resize(numIterations);
134 totalFutures += tinfo.numFutures;
137 Barrier barrier(totalFutures);
139 for (auto& tinfo : threads) {
141 tinfo.thread = std::thread(
142 [numIterations, pinfo, &barrier] () {
143 std::vector<folly::Future<bool>> futures;
144 futures.reserve(pinfo->numFutures);
145 for (uint32_t i = 0; i < numIterations; ++i, ++pinfo->iteration) {
147 for (uint32_t j = 0; j < pinfo->numFutures; ++j) {
148 futures.push_back(barrier.wait());
149 auto nanos = folly::Random::rand32(10 * 1000 * 1000);
151 std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
153 auto results = folly::collect(futures).get();
155 std::count(results.begin(), results.end(), true);
160 for (auto& tinfo : threads) {
162 EXPECT_EQ(numIterations, tinfo.iteration);
165 for (uint32_t i = 0; i < numIterations; ++i) {
166 uint32_t trueCount = 0;
167 for (auto& tinfo : threads) {
168 trueCount += tinfo.trueSeen[i];
170 EXPECT_EQ(1, trueCount);