xref: /freebsd/contrib/kyua/utils/signals/timer_test.cpp (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
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
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.
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
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
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);
121 ATF_TEST_CASE_HEAD(program_seconds)
122 {
123     set_md_var("timeout", "10");
124 }
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);
135 ATF_TEST_CASE_HEAD(program_useconds)
136 {
137     set_md_var("timeout", "10");
138 }
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);
149 ATF_TEST_CASE_HEAD(multiprogram_ordered)
150 {
151     set_md_var("timeout", "20");
152 }
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);
177 ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations)
178 {
179     set_md_var("timeout", "20");
180 }
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);
218 ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some)
219 {
220     set_md_var("timeout", "20");
221 }
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);
259 ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations)
260 {
261     set_md_var("timeout", "20");
262 }
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);
301 ATF_TEST_CASE_HEAD(expire_before_firing)
302 {
303     set_md_var("timeout", "20");
304 }
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);
333 ATF_TEST_CASE_HEAD(reprogram_from_scratch)
334 {
335     set_md_var("timeout", "20");
336 }
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);
361 ATF_TEST_CASE_HEAD(unprogram)
362 {
363     set_md_var("timeout", "10");
364 }
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);
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 }
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 
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