Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/signals/timer.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 <sys/time.h>
33
34
#include <signal.h>
35
}
36
37
#include <cerrno>
38
#include <map>
39
#include <set>
40
#include <vector>
41
42
#include "utils/datetime.hpp"
43
#include "utils/format/macros.hpp"
44
#include "utils/logging/macros.hpp"
45
#include "utils/noncopyable.hpp"
46
#include "utils/optional.ipp"
47
#include "utils/sanity.hpp"
48
#include "utils/signals/exceptions.hpp"
49
#include "utils/signals/interrupts.hpp"
50
#include "utils/signals/programmer.hpp"
51
52
namespace datetime = utils::datetime;
53
namespace signals = utils::signals;
54
55
using utils::none;
56
using utils::optional;
57
58
namespace {
59
60
61
static void sigalrm_handler(const int);
62
63
64
/// Calls setitimer(2) with exception-based error reporting.
65
///
66
/// This does not currently support intervals.
67
///
68
/// \param delta The time to the first activation of the programmed timer.
69
/// \param old_timeval If not NULL, pointer to a timeval into which to store the
70
/// existing system timer.
71
///
72
/// \throw system_error If the call to setitimer(2) fails.
73
static void
74
safe_setitimer(const datetime::delta& delta, itimerval* old_timeval)
75
{
76
::itimerval timeval;
77
timerclear(&timeval.it_interval);
78
timeval.it_value.tv_sec = delta.seconds;
79
timeval.it_value.tv_usec = delta.useconds;
80
81
if (::setitimer(ITIMER_REAL, &timeval, old_timeval) == -1) {
82
const int original_errno = errno;
83
throw signals::system_error("Failed to program system's interval timer",
84
original_errno);
85
}
86
}
87
88
89
/// Deadline scheduler for all user timers on top of the unique system timer.
90
class global_state : utils::noncopyable {
91
/// Collection of active timers.
92
///
93
/// Because this is a collection of pointers, all timers are guaranteed to
94
/// be unique, and we want all of these pointers to be valid.
95
typedef std::set< signals::timer* > timers_set;
96
97
/// Sequence of ordered timers.
98
typedef std::vector< signals::timer* > timers_vector;
99
100
/// Collection of active timestamps by their activation timestamp.
101
///
102
/// This collection is ordered intentionally so that it can be scanned
103
/// sequentially to find either expired or expiring-now timers.
104
typedef std::map< datetime::timestamp, timers_set > timers_by_timestamp_map;
105
106
/// The original timer before any timer was programmed.
107
::itimerval _old_timeval;
108
109
/// Programmer for the SIGALRM handler.
110
std::unique_ptr< signals::programmer > _sigalrm_programmer;
111
112
/// Time of the current activation of the timer.
113
datetime::timestamp _timer_activation;
114
115
/// Mapping of all active timers using their timestamp as the key.
116
timers_by_timestamp_map _all_timers;
117
118
/// Adds a timer to the _all_timers map.
119
///
120
/// \param timer The timer to add.
121
void
122
add_to_all_timers(signals::timer* timer)
123
{
124
timers_set& timers = _all_timers[timer->when()];
125
INV(timers.find(timer) == timers.end());
126
timers.insert(timer);
127
}
128
129
/// Removes a timer from the _all_timers map.
130
///
131
/// This ensures that empty vectors are removed from _all_timers if the
132
/// removal of the timer causes its bucket to be emptied.
133
///
134
/// \param timer The timer to remove.
135
void
136
remove_from_all_timers(signals::timer* timer)
137
{
138
// We may not find the timer in _all_timers if the timer has fired,
139
// because fire() took it out from the map.
140
timers_by_timestamp_map::iterator iter = _all_timers.find(
141
timer->when());
142
if (iter != _all_timers.end()) {
143
timers_set& timers = (*iter).second;
144
INV(timers.find(timer) != timers.end());
145
timers.erase(timer);
146
if (timers.empty()) {
147
_all_timers.erase(iter);
148
}
149
}
150
}
151
152
/// Calculates all timers to execute at this timestamp.
153
///
154
/// \param now The current timestamp.
155
///
156
/// \post _all_timers is updated to contain only the timers that are
157
/// strictly in the future.
158
///
159
/// \return A sequence of valid timers that need to be invoked in the order
160
/// of activation. These are all previously registered timers with
161
/// activations in the past.
162
timers_vector
163
compute_timers_to_run_and_prune_old(
164
const datetime::timestamp& now,
165
const signals::interrupts_inhibiter& /* inhibiter */)
166
{
167
timers_vector to_run;
168
169
timers_by_timestamp_map::iterator iter = _all_timers.begin();
170
while (iter != _all_timers.end() && (*iter).first <= now) {
171
const timers_set& timers = (*iter).second;
172
to_run.insert(to_run.end(), timers.begin(), timers.end());
173
174
// Remove expired entries here so that we can always assume that
175
// the first entry in all_timers corresponds to the next
176
// activation.
177
const timers_by_timestamp_map::iterator previous_iter = iter;
178
++iter;
179
_all_timers.erase(previous_iter);
180
}
181
182
return to_run;
183
}
184
185
/// Adjusts the global system timer to point to the next activation.
186
///
187
/// \param now The current timestamp.
188
///
189
/// \throw system_error If the programming fails.
190
void
191
reprogram_system_timer(
192
const datetime::timestamp& now,
193
const signals::interrupts_inhibiter& /* inhibiter */)
194
{
195
if (_all_timers.empty()) {
196
// Nothing to do. We can reach this case if all the existing timers
197
// are in the past and they all fired. Just ignore the request and
198
// leave the global timer as is.
199
return;
200
}
201
202
// While fire() prunes old entries from the list of timers, it is
203
// possible for this routine to run with "expired" timers (i.e. timers
204
// whose deadline lies in the past but that have not yet fired for
205
// whatever reason that is out of our control) in the list. We have to
206
// iterate until we find the next activation instead of assuming that
207
// the first entry represents the desired value.
208
timers_by_timestamp_map::const_iterator iter = _all_timers.begin();
209
PRE(!(*iter).second.empty());
210
datetime::timestamp next = (*iter).first;
211
while (next < now) {
212
++iter;
213
if (iter == _all_timers.end()) {
214
// Nothing to do. We can reach this case if all the existing
215
// timers are in the past but they have not yet fired.
216
return;
217
}
218
PRE(!(*iter).second.empty());
219
next = (*iter).first;
220
}
221
222
if (next < _timer_activation || now > _timer_activation) {
223
INV(next >= now);
224
const datetime::delta delta = next - now;
225
LD(F("Reprogramming timer; firing on %s; now is %s") % next % now);
226
safe_setitimer(delta, NULL);
227
_timer_activation = next;
228
}
229
}
230
231
public:
232
/// Programs the first timer.
233
///
234
/// The programming of the first timer involves setting up the SIGALRM
235
/// handler and installing a timer handler for the first time, which in turn
236
/// involves keeping track of the old handlers so that we can restore them.
237
///
238
/// \param timer The timer being programmed.
239
/// \param now The current timestamp.
240
///
241
/// \throw system_error If the programming fails.
242
global_state(signals::timer* timer, const datetime::timestamp& now) :
243
_timer_activation(timer->when())
244
{
245
PRE(now < timer->when());
246
247
signals::interrupts_inhibiter inhibiter;
248
249
const datetime::delta delta = timer->when() - now;
250
LD(F("Installing first timer; firing on %s; now is %s") %
251
timer->when() % now);
252
253
_sigalrm_programmer.reset(
254
new signals::programmer(SIGALRM, sigalrm_handler));
255
try {
256
safe_setitimer(delta, &_old_timeval);
257
_timer_activation = timer->when();
258
add_to_all_timers(timer);
259
} catch (...) {
260
_sigalrm_programmer.reset();
261
throw;
262
}
263
}
264
265
/// Unprograms all timers.
266
///
267
/// This clears the global system timer and unsets the SIGALRM handler.
268
~global_state(void)
269
{
270
signals::interrupts_inhibiter inhibiter;
271
272
LD("Unprogramming all timers");
273
274
if (::setitimer(ITIMER_REAL, &_old_timeval, NULL) == -1) {
275
UNREACHABLE_MSG("Failed to restore original timer");
276
}
277
278
_sigalrm_programmer->unprogram();
279
_sigalrm_programmer.reset();
280
}
281
282
/// Programs a new timer, possibly adjusting the global system timer.
283
///
284
/// Programming any timer other than the first one only involves reloading
285
/// the existing timer, not backing up the previous handler nor installing a
286
/// handler for SIGALRM.
287
///
288
/// \param timer The timer being programmed.
289
/// \param now The current timestamp.
290
///
291
/// \throw system_error If the programming fails.
292
void
293
program_new(signals::timer* timer, const datetime::timestamp& now)
294
{
295
signals::interrupts_inhibiter inhibiter;
296
297
add_to_all_timers(timer);
298
reprogram_system_timer(now, inhibiter);
299
}
300
301
/// Unprograms a timer.
302
///
303
/// This removes the timer from the global state and reprograms the global
304
/// system timer if necessary.
305
///
306
/// \param timer The timer to unprogram.
307
///
308
/// \return True if the system interval timer has been reprogrammed to
309
/// another future timer; false if there are no more active timers.
310
bool
311
unprogram(signals::timer* timer)
312
{
313
signals::interrupts_inhibiter inhibiter;
314
315
LD(F("Unprogramming timer; previously firing on %s") % timer->when());
316
317
remove_from_all_timers(timer);
318
if (_all_timers.empty()) {
319
return false;
320
} else {
321
reprogram_system_timer(datetime::timestamp::now(), inhibiter);
322
return true;
323
}
324
}
325
326
/// Executes active timers.
327
///
328
/// Active timers are all those that fire on or before 'now'.
329
///
330
/// \param now The current time.
331
void
332
fire(const datetime::timestamp& now)
333
{
334
timers_vector to_run;
335
{
336
signals::interrupts_inhibiter inhibiter;
337
to_run = compute_timers_to_run_and_prune_old(now, inhibiter);
338
reprogram_system_timer(now, inhibiter);
339
}
340
341
for (timers_vector::iterator iter = to_run.begin();
342
iter != to_run.end(); ++iter) {
343
signals::detail::invoke_do_fired(*iter);
344
}
345
}
346
};
347
348
349
/// Unique instance of the global state.
350
static std::unique_ptr< global_state > globals;
351
352
353
/// SIGALRM handler for the timer implementation.
354
///
355
/// \param signo The signal received; must be SIGALRM.
356
static void
357
sigalrm_handler(const int signo)
358
{
359
PRE(signo == SIGALRM);
360
globals->fire(datetime::timestamp::now());
361
}
362
363
364
} // anonymous namespace
365
366
367
/// Indirection to invoke the private do_fired() method of a timer.
368
///
369
/// \param timer The timer on which to run do_fired().
370
void
371
utils::signals::detail::invoke_do_fired(timer* timer)
372
{
373
timer->do_fired();
374
}
375
376
377
/// Internal implementation for the timer.
378
///
379
/// We assume that there is a 1-1 mapping between timer objects and impl
380
/// objects. If this assumption breaks, then the rest of the code in this
381
/// module breaks as well because we use pointers to the parent timer as the
382
/// identifier of the timer.
383
struct utils::signals::timer::impl : utils::noncopyable {
384
/// Timestamp when this timer is expected to fire.
385
///
386
/// Note that the timer might be processed after this timestamp, so users of
387
/// this field need to check for timers that fire on or before the
388
/// activation time.
389
datetime::timestamp when;
390
391
/// True until unprogram() is called.
392
bool programmed;
393
394
/// Whether this timer has fired already or not.
395
///
396
/// This is updated from an interrupt context, hence why it is marked
397
/// volatile.
398
volatile bool fired;
399
400
/// Constructor.
401
///
402
/// \param when_ Timestamp when this timer is expected to fire.
403
impl(const datetime::timestamp& when_) :
404
when(when_), programmed(true), fired(false)
405
{
406
}
407
408
/// Destructor.
409
~impl(void) {
410
}
411
};
412
413
414
/// Constructor; programs a run-once timer.
415
///
416
/// This programs the global timer and signal handler if this is the first timer
417
/// being installed. Otherwise, reprograms the global timer if this timer
418
/// expires earlier than all other active timers.
419
///
420
/// \param delta The time until the timer fires.
421
signals::timer::timer(const datetime::delta& delta)
422
{
423
signals::interrupts_inhibiter inhibiter;
424
425
const datetime::timestamp now = datetime::timestamp::now();
426
_pimpl.reset(new impl(now + delta));
427
if (globals.get() == NULL) {
428
globals.reset(new global_state(this, now));
429
} else {
430
globals->program_new(this, now);
431
}
432
}
433
434
435
/// Destructor; unprograms the timer if still programmed.
436
///
437
/// Given that this is a destructor and it can't report errors back to the
438
/// caller, the caller must attempt to call unprogram() on its own. This is
439
/// extremely important because, otherwise, expired timers will never run!
440
signals::timer::~timer(void)
441
{
442
signals::interrupts_inhibiter inhibiter;
443
444
if (_pimpl->programmed) {
445
LW("Auto-destroying still-programmed signals::timer object");
446
try {
447
unprogram();
448
} catch (const system_error& e) {
449
UNREACHABLE;
450
}
451
}
452
453
if (!_pimpl->fired) {
454
const datetime::timestamp now = datetime::timestamp::now();
455
if (now > _pimpl->when) {
456
LW("Expired timer never fired; the code never called unprogram()!");
457
}
458
}
459
}
460
461
462
/// Returns the time of the timer activation.
463
///
464
/// \return A timestamp that has no relation to the current time (i.e. can be in
465
/// the future or in the past) nor the timer's activation status.
466
const datetime::timestamp&
467
signals::timer::when(void) const
468
{
469
return _pimpl->when;
470
}
471
472
473
/// Callback for the SIGALRM handler when this timer expires.
474
///
475
/// \warning This is executed from a signal handler context without signals
476
/// inhibited. See signal(7) for acceptable system calls.
477
void
478
signals::timer::do_fired(void)
479
{
480
PRE(!_pimpl->fired);
481
_pimpl->fired = true;
482
callback();
483
}
484
485
486
/// User-provided callback to run when the timer expires.
487
///
488
/// The default callback does nothing. We record the activation of the timer
489
/// separately, which may be appropriate in the majority of the cases.
490
///
491
/// \warning This is executed from a signal handler context without signals
492
/// inhibited. See signal(7) for acceptable system calls.
493
void
494
signals::timer::callback(void)
495
{
496
// Intentionally left blank.
497
}
498
499
500
/// Checks whether the timer has fired already or not.
501
///
502
/// \return Returns true if the timer has fired.
503
bool
504
signals::timer::fired(void) const
505
{
506
return _pimpl->fired;
507
}
508
509
510
/// Unprograms the timer.
511
///
512
/// \pre The timer is programmed (i.e. this can only be called once).
513
///
514
/// \post If the timer never fired asynchronously because the signal delivery
515
/// did not arrive on time, make sure we invoke the timer's callback here.
516
///
517
/// \throw system_error If unprogramming the timer failed.
518
void
519
signals::timer::unprogram(void)
520
{
521
signals::interrupts_inhibiter inhibiter;
522
523
if (!_pimpl->programmed) {
524
// We cannot assert that the timer is not programmed because it might
525
// have been unprogrammed asynchronously between the time we called
526
// unprogram() and the time we reach this. Simply return in this case.
527
LD("Called unprogram on already-unprogrammed timer; possibly just "
528
"a race");
529
return;
530
}
531
532
if (!globals->unprogram(this)) {
533
globals.reset();
534
}
535
_pimpl->programmed = false;
536
537
// Handle the case where the timer has expired before we ever got its
538
// corresponding signal. Do so by invoking its callback now.
539
if (!_pimpl->fired) {
540
const datetime::timestamp now = datetime::timestamp::now();
541
if (now > _pimpl->when) {
542
LW(F("Firing expired timer on destruction (was to fire on %s)") %
543
_pimpl->when);
544
do_fired();
545
}
546
}
547
}
548
549