Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/signals/timer_test.cpp
48199 views
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
428