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
31extern "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
49namespace datetime = utils::datetime;
50namespace signals = utils::signals;
51
52
53namespace {
54
55
56/// A timer that inserts an element into a vector on activation.
57class 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
72public:
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.
87static void
88null_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.
96static void
97wait_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
120ATF_TEST_CASE(program_seconds);
121ATF_TEST_CASE_HEAD(program_seconds)
122{
123    set_md_var("timeout", "10");
124}
125ATF_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
134ATF_TEST_CASE(program_useconds);
135ATF_TEST_CASE_HEAD(program_useconds)
136{
137    set_md_var("timeout", "10");
138}
139ATF_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
148ATF_TEST_CASE(multiprogram_ordered);
149ATF_TEST_CASE_HEAD(multiprogram_ordered)
150{
151    set_md_var("timeout", "20");
152}
153ATF_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
176ATF_TEST_CASE(multiprogram_reorder_next_activations);
177ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations)
178{
179    set_md_var("timeout", "20");
180}
181ATF_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
217ATF_TEST_CASE(multiprogram_and_cancel_some);
218ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some)
219{
220    set_md_var("timeout", "20");
221}
222ATF_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
258ATF_TEST_CASE(multiprogram_and_expire_before_activations);
259ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations)
260{
261    set_md_var("timeout", "20");
262}
263ATF_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
300ATF_TEST_CASE(expire_before_firing);
301ATF_TEST_CASE_HEAD(expire_before_firing)
302{
303    set_md_var("timeout", "20");
304}
305ATF_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
332ATF_TEST_CASE(reprogram_from_scratch);
333ATF_TEST_CASE_HEAD(reprogram_from_scratch)
334{
335    set_md_var("timeout", "20");
336}
337ATF_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
360ATF_TEST_CASE(unprogram);
361ATF_TEST_CASE_HEAD(unprogram)
362{
363    set_md_var("timeout", "10");
364}
365ATF_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
374ATF_TEST_CASE(infinitesimal);
375ATF_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}
383ATF_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
414ATF_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