ece8c0f6ae20fc1bd264fd58fa3f0bd977ad14d6
[folly.git] / folly / experimental / test / EventCountTest.cpp
1 /*
2  * Copyright 2014 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 <random>
21 #include <glog/logging.h>
22 #include <gtest/gtest.h>
23
24 #include "folly/Random.h"
25 #include "folly/Benchmark.h"
26
27 using namespace folly;
28
29 namespace {
30
31 class Semaphore {
32  public:
33   explicit Semaphore(int v=0) : value_(v) { }
34
35   void down() {
36     ec_.await([this] { return tryDown(); });
37   }
38
39   void up() {
40     ++value_;
41     ec_.notifyAll();
42   }
43
44   int value() const {
45     return value_;
46   }
47
48  private:
49   bool tryDown() {
50     for (;;) {
51       int v = value_;
52       if (v == 0) {
53         return false;
54       }
55       if (value_.compare_exchange_weak(v, v-1)) {
56         return true;
57       }
58     }
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.push_back(std::make_pair(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 }
127
128 int main(int argc, char *argv[]) {
129   testing::InitGoogleTest(&argc, argv);
130   google::ParseCommandLineFlags(&argc, &argv, true);
131   auto ret = RUN_ALL_TESTS();
132   if (!ret) {
133     folly::runBenchmarksOnFlag();
134   }
135   return ret;
136 }
137