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> |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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. |
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 | |
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 | } |