1 // Copyright 2010 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/signals/timer.hpp"
30
31 extern "C" {
32 #include <signal.h>
33 #include <unistd.h>
34 }
35
36 #include <cstddef>
37 #include <iostream>
38 #include <vector>
39
40 #include <atf-c++.hpp>
41
42 #include "utils/datetime.hpp"
43 #include "utils/defs.hpp"
44 #include "utils/format/containers.ipp"
45 #include "utils/format/macros.hpp"
46 #include "utils/signals/interrupts.hpp"
47 #include "utils/signals/programmer.hpp"
48
49 namespace datetime = utils::datetime;
50 namespace signals = utils::signals;
51
52
53 namespace {
54
55
56 /// A timer that inserts an element into a vector on activation.
57 class delayed_inserter : public signals::timer {
58 /// Vector into which to insert the element.
59 std::vector< int >& _destination;
60
61 /// Element to insert into _destination on activation.
62 const int _item;
63
64 /// Timer activation callback.
65 void
callback(void)66 callback(void)
67 {
68 signals::interrupts_inhibiter inhibiter;
69 _destination.push_back(_item);
70 }
71
72 public:
73 /// Constructor.
74 ///
75 /// \param delta Time to the timer activation.
76 /// \param destination Vector into which to insert the element.
77 /// \param item Element to insert into destination on activation.
delayed_inserter(const datetime::delta & delta,std::vector<int> & destination,const int item)78 delayed_inserter(const datetime::delta& delta,
79 std::vector< int >& destination, const int item) :
80 signals::timer(delta), _destination(destination), _item(item)
81 {
82 }
83 };
84
85
86 /// Signal handler that does nothing.
87 static void
null_handler(const int)88 null_handler(const int /* signo */)
89 {
90 }
91
92
93 /// Waits for the activation of all given timers.
94 ///
95 /// \param timers Pointers to all the timers to wait for.
96 static void
wait_timers(const std::vector<signals::timer * > & timers)97 wait_timers(const std::vector< signals::timer* >& timers)
98 {
99 std::size_t n_fired, old_n_fired = 0;
100 do {
101 n_fired = 0;
102 for (std::vector< signals::timer* >::const_iterator
103 iter = timers.begin(); iter != timers.end(); ++iter) {
104 const signals::timer* timer = *iter;
105 if (timer->fired())
106 ++n_fired;
107 }
108 if (old_n_fired < n_fired) {
109 std::cout << "Waiting; " << n_fired << " timers fired so far\n";
110 old_n_fired = n_fired;
111 }
112 ::usleep(100);
113 } while (n_fired < timers.size());
114 }
115
116
117 } // anonymous namespace
118
119
120 ATF_TEST_CASE(program_seconds);
ATF_TEST_CASE_HEAD(program_seconds)121 ATF_TEST_CASE_HEAD(program_seconds)
122 {
123 set_md_var("timeout", "10");
124 }
ATF_TEST_CASE_BODY(program_seconds)125 ATF_TEST_CASE_BODY(program_seconds)
126 {
127 signals::timer timer(datetime::delta(1, 0));
128 ATF_REQUIRE(!timer.fired());
129 while (!timer.fired())
130 ::usleep(1000);
131 }
132
133
134 ATF_TEST_CASE(program_useconds);
ATF_TEST_CASE_HEAD(program_useconds)135 ATF_TEST_CASE_HEAD(program_useconds)
136 {
137 set_md_var("timeout", "10");
138 }
ATF_TEST_CASE_BODY(program_useconds)139 ATF_TEST_CASE_BODY(program_useconds)
140 {
141 signals::timer timer(datetime::delta(0, 500000));
142 ATF_REQUIRE(!timer.fired());
143 while (!timer.fired())
144 ::usleep(1000);
145 }
146
147
148 ATF_TEST_CASE(multiprogram_ordered);
ATF_TEST_CASE_HEAD(multiprogram_ordered)149 ATF_TEST_CASE_HEAD(multiprogram_ordered)
150 {
151 set_md_var("timeout", "20");
152 }
ATF_TEST_CASE_BODY(multiprogram_ordered)153 ATF_TEST_CASE_BODY(multiprogram_ordered)
154 {
155 static const std::size_t n_timers = 100;
156
157 std::vector< signals::timer* > timers;
158 std::vector< int > items, exp_items;
159
160 const int initial_delay_ms = 1000000;
161 for (std::size_t i = 0; i < n_timers; ++i) {
162 exp_items.push_back(i);
163
164 timers.push_back(new delayed_inserter(
165 datetime::delta(0, initial_delay_ms + (i + 1) * 10000),
166 items, i));
167 ATF_REQUIRE(!timers[i]->fired());
168 }
169
170 wait_timers(timers);
171
172 ATF_REQUIRE_EQ(exp_items, items);
173 }
174
175
176 ATF_TEST_CASE(multiprogram_reorder_next_activations);
ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations)177 ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations)
178 {
179 set_md_var("timeout", "20");
180 }
ATF_TEST_CASE_BODY(multiprogram_reorder_next_activations)181 ATF_TEST_CASE_BODY(multiprogram_reorder_next_activations)
182 {
183 std::vector< signals::timer* > timers;
184 std::vector< int > items;
185
186 // First timer with an activation in the future.
187 timers.push_back(new delayed_inserter(
188 datetime::delta(0, 100000), items, 1));
189 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
190
191 // Timer with an activation earlier than the previous one.
192 timers.push_back(new delayed_inserter(
193 datetime::delta(0, 50000), items, 2));
194 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
195
196 // Timer with an activation later than all others.
197 timers.push_back(new delayed_inserter(
198 datetime::delta(0, 200000), items, 3));
199 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
200
201 // Timer with an activation in between.
202 timers.push_back(new delayed_inserter(
203 datetime::delta(0, 150000), items, 4));
204 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
205
206 wait_timers(timers);
207
208 std::vector< int > exp_items;
209 exp_items.push_back(2);
210 exp_items.push_back(1);
211 exp_items.push_back(4);
212 exp_items.push_back(3);
213 ATF_REQUIRE_EQ(exp_items, items);
214 }
215
216
217 ATF_TEST_CASE(multiprogram_and_cancel_some);
ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some)218 ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some)
219 {
220 set_md_var("timeout", "20");
221 }
ATF_TEST_CASE_BODY(multiprogram_and_cancel_some)222 ATF_TEST_CASE_BODY(multiprogram_and_cancel_some)
223 {
224 std::vector< signals::timer* > timers;
225 std::vector< int > items;
226
227 // First timer with an activation in the future.
228 timers.push_back(new delayed_inserter(
229 datetime::delta(0, 100000), items, 1));
230
231 // Timer with an activation earlier than the previous one.
232 timers.push_back(new delayed_inserter(
233 datetime::delta(0, 50000), items, 2));
234
235 // Timer with an activation later than all others.
236 timers.push_back(new delayed_inserter(
237 datetime::delta(0, 200000), items, 3));
238
239 // Timer with an activation in between.
240 timers.push_back(new delayed_inserter(
241 datetime::delta(0, 150000), items, 4));
242
243 // Cancel the first timer to reprogram next activation.
244 timers[1]->unprogram(); delete timers[1]; timers.erase(timers.begin() + 1);
245
246 // Cancel another timer without reprogramming next activation.
247 timers[2]->unprogram(); delete timers[2]; timers.erase(timers.begin() + 2);
248
249 wait_timers(timers);
250
251 std::vector< int > exp_items;
252 exp_items.push_back(1);
253 exp_items.push_back(3);
254 ATF_REQUIRE_EQ(exp_items, items);
255 }
256
257
258 ATF_TEST_CASE(multiprogram_and_expire_before_activations);
ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations)259 ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations)
260 {
261 set_md_var("timeout", "20");
262 }
ATF_TEST_CASE_BODY(multiprogram_and_expire_before_activations)263 ATF_TEST_CASE_BODY(multiprogram_and_expire_before_activations)
264 {
265 std::vector< signals::timer* > timers;
266 std::vector< int > items;
267
268 {
269 signals::interrupts_inhibiter inhibiter;
270
271 // First timer with an activation in the future.
272 timers.push_back(new delayed_inserter(
273 datetime::delta(0, 100000), items, 1));
274 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
275
276 // Timer with an activation earlier than the previous one.
277 timers.push_back(new delayed_inserter(
278 datetime::delta(0, 50000), items, 2));
279 ATF_REQUIRE(!timers[timers.size() - 1]->fired());
280
281 ::sleep(1);
282
283 // Timer with an activation later than all others.
284 timers.push_back(new delayed_inserter(
285 datetime::delta(0, 200000), items, 3));
286
287 ::sleep(1);
288 }
289
290 wait_timers(timers);
291
292 std::vector< int > exp_items;
293 exp_items.push_back(2);
294 exp_items.push_back(1);
295 exp_items.push_back(3);
296 ATF_REQUIRE_EQ(exp_items, items);
297 }
298
299
300 ATF_TEST_CASE(expire_before_firing);
ATF_TEST_CASE_HEAD(expire_before_firing)301 ATF_TEST_CASE_HEAD(expire_before_firing)
302 {
303 set_md_var("timeout", "20");
304 }
ATF_TEST_CASE_BODY(expire_before_firing)305 ATF_TEST_CASE_BODY(expire_before_firing)
306 {
307 std::vector< int > items;
308
309 // The code below causes a signal to go pending. Make sure we ignore it
310 // when we unblock signals.
311 signals::programmer sigalrm(SIGALRM, null_handler);
312
313 {
314 signals::interrupts_inhibiter inhibiter;
315
316 delayed_inserter* timer = new delayed_inserter(
317 datetime::delta(0, 1000), items, 1234);
318 ::sleep(1);
319 // Interrupts are inhibited so we never got a chance to execute the
320 // timer before it was destroyed. However, the handler should run
321 // regardless at some point, possibly during deletion.
322 timer->unprogram();
323 delete timer;
324 }
325
326 std::vector< int > exp_items;
327 exp_items.push_back(1234);
328 ATF_REQUIRE_EQ(exp_items, items);
329 }
330
331
332 ATF_TEST_CASE(reprogram_from_scratch);
ATF_TEST_CASE_HEAD(reprogram_from_scratch)333 ATF_TEST_CASE_HEAD(reprogram_from_scratch)
334 {
335 set_md_var("timeout", "20");
336 }
ATF_TEST_CASE_BODY(reprogram_from_scratch)337 ATF_TEST_CASE_BODY(reprogram_from_scratch)
338 {
339 std::vector< int > items;
340
341 delayed_inserter* timer1 = new delayed_inserter(
342 datetime::delta(0, 100000), items, 1);
343 timer1->unprogram(); delete timer1;
344
345 // All constructed timers are now dead, so the interval timer should have
346 // been reprogrammed. Let's start over.
347
348 delayed_inserter* timer2 = new delayed_inserter(
349 datetime::delta(0, 200000), items, 2);
350 while (!timer2->fired())
351 ::usleep(1000);
352 timer2->unprogram(); delete timer2;
353
354 std::vector< int > exp_items;
355 exp_items.push_back(2);
356 ATF_REQUIRE_EQ(exp_items, items);
357 }
358
359
360 ATF_TEST_CASE(unprogram);
ATF_TEST_CASE_HEAD(unprogram)361 ATF_TEST_CASE_HEAD(unprogram)
362 {
363 set_md_var("timeout", "10");
364 }
ATF_TEST_CASE_BODY(unprogram)365 ATF_TEST_CASE_BODY(unprogram)
366 {
367 signals::timer timer(datetime::delta(0, 500000));
368 timer.unprogram();
369 usleep(500000);
370 ATF_REQUIRE(!timer.fired());
371 }
372
373
374 ATF_TEST_CASE(infinitesimal);
ATF_TEST_CASE_HEAD(infinitesimal)375 ATF_TEST_CASE_HEAD(infinitesimal)
376 {
377 set_md_var("descr", "Ensure that the ordering in which the signal, the "
378 "timer and the global state are programmed is correct; do so "
379 "by setting an extremely small delay for the timer hoping that "
380 "it can trigger such conditions");
381 set_md_var("timeout", "10");
382 }
ATF_TEST_CASE_BODY(infinitesimal)383 ATF_TEST_CASE_BODY(infinitesimal)
384 {
385 const std::size_t rounds = 100;
386 const std::size_t exp_good = 90;
387
388 std::size_t good = 0;
389 for (std::size_t i = 0; i < rounds; i++) {
390 signals::timer timer(datetime::delta(0, 1));
391
392 // From the setitimer(2) documentation:
393 //
394 // Time values smaller than the resolution of the system clock are
395 // rounded up to this resolution (typically 10 milliseconds).
396 //
397 // We don't know what this resolution is but we must wait for longer
398 // than we programmed; do a rough guess and hope it is good. This may
399 // be obviously wrong and thus lead to mysterious test failures in some
400 // systems, hence why we only expect a percentage of successes below.
401 // Still, we can fail...
402 ::usleep(1000);
403
404 if (timer.fired())
405 ++good;
406 timer.unprogram();
407 }
408 std::cout << F("Ran %s tests, %s passed; threshold is %s\n")
409 % rounds % good % exp_good;
410 ATF_REQUIRE(good >= exp_good);
411 }
412
413
ATF_INIT_TEST_CASES(tcs)414 ATF_INIT_TEST_CASES(tcs)
415 {
416 ATF_ADD_TEST_CASE(tcs, program_seconds);
417 ATF_ADD_TEST_CASE(tcs, program_useconds);
418 ATF_ADD_TEST_CASE(tcs, multiprogram_ordered);
419 ATF_ADD_TEST_CASE(tcs, multiprogram_reorder_next_activations);
420 ATF_ADD_TEST_CASE(tcs, multiprogram_and_cancel_some);
421 ATF_ADD_TEST_CASE(tcs, multiprogram_and_expire_before_activations);
422 ATF_ADD_TEST_CASE(tcs, expire_before_firing);
423 ATF_ADD_TEST_CASE(tcs, reprogram_from_scratch);
424 ATF_ADD_TEST_CASE(tcs, unprogram);
425 ATF_ADD_TEST_CASE(tcs, infinitesimal);
426 }
427