• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Marl Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "marl/event.h"
16 #include "marl/defer.h"
17 #include "marl/waitgroup.h"
18 
19 #include "marl_test.h"
20 
21 #include <array>
22 
23 namespace std {
24 namespace chrono {
25 template <typename Rep, typename Period>
operator <<(std::ostream & os,const duration<Rep,Period> & d)26 std::ostream& operator<<(std::ostream& os, const duration<Rep, Period>& d) {
27   return os << chrono::duration_cast<chrono::microseconds>(d).count() << "ms";
28 }
29 }  // namespace chrono
30 }  // namespace std
31 
TEST_P(WithBoundScheduler,EventIsSignalled)32 TEST_P(WithBoundScheduler, EventIsSignalled) {
33   for (auto mode : {marl::Event::Mode::Manual, marl::Event::Mode::Auto}) {
34     auto event = marl::Event(mode);
35     ASSERT_EQ(event.isSignalled(), false);
36     event.signal();
37     ASSERT_EQ(event.isSignalled(), true);
38     ASSERT_EQ(event.isSignalled(), true);
39     event.clear();
40     ASSERT_EQ(event.isSignalled(), false);
41   }
42 }
43 
TEST_P(WithBoundScheduler,EventAutoTest)44 TEST_P(WithBoundScheduler, EventAutoTest) {
45   auto event = marl::Event(marl::Event::Mode::Auto);
46   ASSERT_EQ(event.test(), false);
47   event.signal();
48   ASSERT_EQ(event.test(), true);
49   ASSERT_EQ(event.test(), false);
50 }
51 
TEST_P(WithBoundScheduler,EventManualTest)52 TEST_P(WithBoundScheduler, EventManualTest) {
53   auto event = marl::Event(marl::Event::Mode::Manual);
54   ASSERT_EQ(event.test(), false);
55   event.signal();
56   ASSERT_EQ(event.test(), true);
57   ASSERT_EQ(event.test(), true);
58 }
59 
TEST_P(WithBoundScheduler,EventAutoWait)60 TEST_P(WithBoundScheduler, EventAutoWait) {
61   std::atomic<int> counter = {0};
62   auto event = marl::Event(marl::Event::Mode::Auto);
63   auto done = marl::Event(marl::Event::Mode::Auto);
64 
65   for (int i = 0; i < 3; i++) {
66     marl::schedule([=, &counter] {
67       event.wait();
68       counter++;
69       done.signal();
70     });
71   }
72 
73   ASSERT_EQ(counter.load(), 0);
74   event.signal();
75   done.wait();
76   ASSERT_EQ(counter.load(), 1);
77   event.signal();
78   done.wait();
79   ASSERT_EQ(counter.load(), 2);
80   event.signal();
81   done.wait();
82   ASSERT_EQ(counter.load(), 3);
83 }
84 
TEST_P(WithBoundScheduler,EventManualWait)85 TEST_P(WithBoundScheduler, EventManualWait) {
86   std::atomic<int> counter = {0};
87   auto event = marl::Event(marl::Event::Mode::Manual);
88   auto wg = marl::WaitGroup(3);
89   for (int i = 0; i < 3; i++) {
90     marl::schedule([=, &counter] {
91       event.wait();
92       counter++;
93       wg.done();
94     });
95   }
96   event.signal();
97   wg.wait();
98   ASSERT_EQ(counter.load(), 3);
99 }
100 
TEST_P(WithBoundScheduler,EventSequence)101 TEST_P(WithBoundScheduler, EventSequence) {
102   for (auto mode : {marl::Event::Mode::Manual, marl::Event::Mode::Auto}) {
103     std::string sequence;
104     auto eventA = marl::Event(mode);
105     auto eventB = marl::Event(mode);
106     auto eventC = marl::Event(mode);
107     auto done = marl::Event(mode);
108     marl::schedule([=, &sequence] {
109       eventB.wait();
110       sequence += "B";
111       eventC.signal();
112     });
113     marl::schedule([=, &sequence] {
114       eventA.wait();
115       sequence += "A";
116       eventB.signal();
117     });
118     marl::schedule([=, &sequence] {
119       eventC.wait();
120       sequence += "C";
121       done.signal();
122     });
123     ASSERT_EQ(sequence, "");
124     eventA.signal();
125     done.wait();
126     ASSERT_EQ(sequence, "ABC");
127   }
128 }
129 
TEST_P(WithBoundScheduler,EventWaitForUnblocked)130 TEST_P(WithBoundScheduler, EventWaitForUnblocked) {
131   auto event = marl::Event(marl::Event::Mode::Manual);
132   auto wg = marl::WaitGroup(1000);
133   for (int i = 0; i < 1000; i++) {
134     marl::schedule([=] {
135       defer(wg.done());
136       auto duration = std::chrono::seconds(10);
137       event.wait_for(duration);
138     });
139   }
140   event.signal();  // unblock
141   wg.wait();
142 }
143 
TEST_P(WithBoundScheduler,EventWaitForTimeTaken)144 TEST_P(WithBoundScheduler, EventWaitForTimeTaken) {
145   auto event = marl::Event(marl::Event::Mode::Auto);
146   auto wg = marl::WaitGroup(1000);
147   for (int i = 0; i < 1000; i++) {
148     marl::schedule([=] {
149       defer(wg.done());
150       auto duration = std::chrono::milliseconds(10);
151       auto start = std::chrono::system_clock::now();
152       auto triggered = event.wait_for(duration);
153       auto end = std::chrono::system_clock::now();
154       ASSERT_FALSE(triggered);
155       ASSERT_GE(end - start, duration);
156     });
157   }
158   wg.wait();
159 }
160 
TEST_P(WithBoundScheduler,EventWaitUntilUnblocked)161 TEST_P(WithBoundScheduler, EventWaitUntilUnblocked) {
162   auto event = marl::Event(marl::Event::Mode::Manual);
163   auto wg = marl::WaitGroup(1000);
164   for (int i = 0; i < 1000; i++) {
165     marl::schedule([=] {
166       defer(wg.done());
167       auto duration = std::chrono::seconds(10);
168       auto start = std::chrono::system_clock::now();
169       event.wait_until(start + duration);
170     });
171   }
172   event.signal();  // unblock
173   wg.wait();
174 }
175 
TEST_P(WithBoundScheduler,EventWaitUntilTimeTaken)176 TEST_P(WithBoundScheduler, EventWaitUntilTimeTaken) {
177   auto event = marl::Event(marl::Event::Mode::Auto);
178   auto wg = marl::WaitGroup(1000);
179   for (int i = 0; i < 1000; i++) {
180     marl::schedule([=] {
181       defer(wg.done());
182       auto duration = std::chrono::milliseconds(10);
183       auto start = std::chrono::system_clock::now();
184       auto triggered = event.wait_until(start + duration);
185       auto end = std::chrono::system_clock::now();
186       ASSERT_FALSE(triggered);
187       ASSERT_GE(end - start, duration);
188     });
189   }
190   wg.wait();
191 }
192 
193 // EventWaitStressTest spins up a whole lot of wait_fors(), unblocking some
194 // with timeouts and some with an event signal, and then let's all the workers
195 // go to idle before repeating.
196 // This is testing to ensure that the scheduler handles timeouts correctly when
197 // they are early-unblocked. Specifically, this is to test that fibers are
198 // not double-placed into the idle or working lists.
TEST_P(WithBoundScheduler,EventWaitStressTest)199 TEST_P(WithBoundScheduler, EventWaitStressTest) {
200   auto event = marl::Event(marl::Event::Mode::Manual);
201   for (int i = 0; i < 10; i++) {
202     auto wg = marl::WaitGroup(100);
203     for (int j = 0; j < 100; j++) {
204       marl::schedule([=] {
205         defer(wg.done());
206         event.wait_for(std::chrono::milliseconds(j));
207       });
208     }
209     std::this_thread::sleep_for(std::chrono::milliseconds(50));
210     event.signal();  // unblock
211     wg.wait();
212   }
213 }
214 
TEST_P(WithBoundScheduler,EventAny)215 TEST_P(WithBoundScheduler, EventAny) {
216   for (int i = 0; i < 3; i++) {
217     std::array<marl::Event, 3> events = {
218         marl::Event(marl::Event::Mode::Auto),
219         marl::Event(marl::Event::Mode::Auto),
220         marl::Event(marl::Event::Mode::Auto),
221     };
222     auto any = marl::Event::any(events.begin(), events.end());
223     events[i].signal();
224     ASSERT_TRUE(any.isSignalled());
225   }
226 }