Sort #include lines
[folly.git] / folly / experimental / test / EventCountTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
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/experimental/EventCount.h>
18
19 #include <algorithm>
20 #include <atomic>
21 #include <random>
22 #include <thread>
23 #include <vector>
24
25 #include <glog/logging.h>
26
27 #include <folly/Random.h>
28 #include <folly/portability/GTest.h>
29
30 using namespace folly;
31
32 namespace {
33
34 class Semaphore {
35  public:
36   explicit Semaphore(int v=0) : value_(v) { }
37
38   void down() {
39     ec_.await([this] { return tryDown(); });
40   }
41
42   void up() {
43     ++value_;
44     ec_.notifyAll();
45   }
46
47   int value() const {
48     return value_;
49   }
50
51  private:
52   bool tryDown() {
53     for (int v = value_; v != 0;) {
54       if (value_.compare_exchange_weak(v, v-1)) {
55         return true;
56       }
57     }
58     return false;
59   }
60
61   std::atomic<int> value_;
62   EventCount ec_;
63 };
64
65 template <class T, class Random>
66 void randomPartition(Random& random, T key, int n,
67                      std::vector<std::pair<T, int>>& out) {
68   while (n != 0) {
69     int m = std::min(n, 1000);
70     std::uniform_int_distribution<uint32_t> u(1, m);
71     int cut = u(random);
72     out.emplace_back(key, cut);
73     n -= cut;
74   }
75 }
76
77 }  // namespace
78
79 TEST(EventCount, Simple) {
80   // We're basically testing for no deadlock.
81   static const size_t count = 300000;
82
83   enum class Op {
84     UP,
85     DOWN
86   };
87   std::vector<std::pair<Op, int>> ops;
88   std::mt19937 rnd(randomNumberSeed());
89   randomPartition(rnd, Op::UP, count, ops);
90   size_t uppers = ops.size();
91   randomPartition(rnd, Op::DOWN, count, ops);
92   size_t downers = ops.size() - uppers;
93   VLOG(1) << "Using " << ops.size() << " threads: uppers=" << uppers
94           << " downers=" << downers << " sem_count=" << count;
95
96   std::random_shuffle(ops.begin(), ops.end());
97
98   std::vector<std::thread> threads;
99   threads.reserve(ops.size());
100
101   Semaphore sem;
102   for (auto& op : ops) {
103     int n = op.second;
104     if (op.first == Op::UP) {
105       auto fn = [&sem, n] () mutable {
106         while (n--) {
107           sem.up();
108         }
109       };
110       threads.push_back(std::thread(fn));
111     } else {
112       auto fn = [&sem, n] () mutable {
113         while (n--) {
114           sem.down();
115         }
116       };
117       threads.push_back(std::thread(fn));
118     }
119   }
120
121   for (auto& thread : threads) {
122     thread.join();
123   }
124
125   EXPECT_EQ(0, sem.value());
126 }