Path: blob/main/contrib/kyua/utils/signals/timer.cpp
48199 views
// Copyright 2010 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/signals/timer.hpp"2930extern "C" {31#include <sys/time.h>3233#include <signal.h>34}3536#include <cerrno>37#include <map>38#include <set>39#include <vector>4041#include "utils/datetime.hpp"42#include "utils/format/macros.hpp"43#include "utils/logging/macros.hpp"44#include "utils/noncopyable.hpp"45#include "utils/optional.ipp"46#include "utils/sanity.hpp"47#include "utils/signals/exceptions.hpp"48#include "utils/signals/interrupts.hpp"49#include "utils/signals/programmer.hpp"5051namespace datetime = utils::datetime;52namespace signals = utils::signals;5354using utils::none;55using utils::optional;5657namespace {585960static void sigalrm_handler(const int);616263/// Calls setitimer(2) with exception-based error reporting.64///65/// This does not currently support intervals.66///67/// \param delta The time to the first activation of the programmed timer.68/// \param old_timeval If not NULL, pointer to a timeval into which to store the69/// existing system timer.70///71/// \throw system_error If the call to setitimer(2) fails.72static void73safe_setitimer(const datetime::delta& delta, itimerval* old_timeval)74{75::itimerval timeval;76timerclear(&timeval.it_interval);77timeval.it_value.tv_sec = delta.seconds;78timeval.it_value.tv_usec = delta.useconds;7980if (::setitimer(ITIMER_REAL, &timeval, old_timeval) == -1) {81const int original_errno = errno;82throw signals::system_error("Failed to program system's interval timer",83original_errno);84}85}868788/// Deadline scheduler for all user timers on top of the unique system timer.89class global_state : utils::noncopyable {90/// Collection of active timers.91///92/// Because this is a collection of pointers, all timers are guaranteed to93/// be unique, and we want all of these pointers to be valid.94typedef std::set< signals::timer* > timers_set;9596/// Sequence of ordered timers.97typedef std::vector< signals::timer* > timers_vector;9899/// Collection of active timestamps by their activation timestamp.100///101/// This collection is ordered intentionally so that it can be scanned102/// sequentially to find either expired or expiring-now timers.103typedef std::map< datetime::timestamp, timers_set > timers_by_timestamp_map;104105/// The original timer before any timer was programmed.106::itimerval _old_timeval;107108/// Programmer for the SIGALRM handler.109std::unique_ptr< signals::programmer > _sigalrm_programmer;110111/// Time of the current activation of the timer.112datetime::timestamp _timer_activation;113114/// Mapping of all active timers using their timestamp as the key.115timers_by_timestamp_map _all_timers;116117/// Adds a timer to the _all_timers map.118///119/// \param timer The timer to add.120void121add_to_all_timers(signals::timer* timer)122{123timers_set& timers = _all_timers[timer->when()];124INV(timers.find(timer) == timers.end());125timers.insert(timer);126}127128/// Removes a timer from the _all_timers map.129///130/// This ensures that empty vectors are removed from _all_timers if the131/// removal of the timer causes its bucket to be emptied.132///133/// \param timer The timer to remove.134void135remove_from_all_timers(signals::timer* timer)136{137// We may not find the timer in _all_timers if the timer has fired,138// because fire() took it out from the map.139timers_by_timestamp_map::iterator iter = _all_timers.find(140timer->when());141if (iter != _all_timers.end()) {142timers_set& timers = (*iter).second;143INV(timers.find(timer) != timers.end());144timers.erase(timer);145if (timers.empty()) {146_all_timers.erase(iter);147}148}149}150151/// Calculates all timers to execute at this timestamp.152///153/// \param now The current timestamp.154///155/// \post _all_timers is updated to contain only the timers that are156/// strictly in the future.157///158/// \return A sequence of valid timers that need to be invoked in the order159/// of activation. These are all previously registered timers with160/// activations in the past.161timers_vector162compute_timers_to_run_and_prune_old(163const datetime::timestamp& now,164const signals::interrupts_inhibiter& /* inhibiter */)165{166timers_vector to_run;167168timers_by_timestamp_map::iterator iter = _all_timers.begin();169while (iter != _all_timers.end() && (*iter).first <= now) {170const timers_set& timers = (*iter).second;171to_run.insert(to_run.end(), timers.begin(), timers.end());172173// Remove expired entries here so that we can always assume that174// the first entry in all_timers corresponds to the next175// activation.176const timers_by_timestamp_map::iterator previous_iter = iter;177++iter;178_all_timers.erase(previous_iter);179}180181return to_run;182}183184/// Adjusts the global system timer to point to the next activation.185///186/// \param now The current timestamp.187///188/// \throw system_error If the programming fails.189void190reprogram_system_timer(191const datetime::timestamp& now,192const signals::interrupts_inhibiter& /* inhibiter */)193{194if (_all_timers.empty()) {195// Nothing to do. We can reach this case if all the existing timers196// are in the past and they all fired. Just ignore the request and197// leave the global timer as is.198return;199}200201// While fire() prunes old entries from the list of timers, it is202// possible for this routine to run with "expired" timers (i.e. timers203// whose deadline lies in the past but that have not yet fired for204// whatever reason that is out of our control) in the list. We have to205// iterate until we find the next activation instead of assuming that206// the first entry represents the desired value.207timers_by_timestamp_map::const_iterator iter = _all_timers.begin();208PRE(!(*iter).second.empty());209datetime::timestamp next = (*iter).first;210while (next < now) {211++iter;212if (iter == _all_timers.end()) {213// Nothing to do. We can reach this case if all the existing214// timers are in the past but they have not yet fired.215return;216}217PRE(!(*iter).second.empty());218next = (*iter).first;219}220221if (next < _timer_activation || now > _timer_activation) {222INV(next >= now);223const datetime::delta delta = next - now;224LD(F("Reprogramming timer; firing on %s; now is %s") % next % now);225safe_setitimer(delta, NULL);226_timer_activation = next;227}228}229230public:231/// Programs the first timer.232///233/// The programming of the first timer involves setting up the SIGALRM234/// handler and installing a timer handler for the first time, which in turn235/// involves keeping track of the old handlers so that we can restore them.236///237/// \param timer The timer being programmed.238/// \param now The current timestamp.239///240/// \throw system_error If the programming fails.241global_state(signals::timer* timer, const datetime::timestamp& now) :242_timer_activation(timer->when())243{244PRE(now < timer->when());245246signals::interrupts_inhibiter inhibiter;247248const datetime::delta delta = timer->when() - now;249LD(F("Installing first timer; firing on %s; now is %s") %250timer->when() % now);251252_sigalrm_programmer.reset(253new signals::programmer(SIGALRM, sigalrm_handler));254try {255safe_setitimer(delta, &_old_timeval);256_timer_activation = timer->when();257add_to_all_timers(timer);258} catch (...) {259_sigalrm_programmer.reset();260throw;261}262}263264/// Unprograms all timers.265///266/// This clears the global system timer and unsets the SIGALRM handler.267~global_state(void)268{269signals::interrupts_inhibiter inhibiter;270271LD("Unprogramming all timers");272273if (::setitimer(ITIMER_REAL, &_old_timeval, NULL) == -1) {274UNREACHABLE_MSG("Failed to restore original timer");275}276277_sigalrm_programmer->unprogram();278_sigalrm_programmer.reset();279}280281/// Programs a new timer, possibly adjusting the global system timer.282///283/// Programming any timer other than the first one only involves reloading284/// the existing timer, not backing up the previous handler nor installing a285/// handler for SIGALRM.286///287/// \param timer The timer being programmed.288/// \param now The current timestamp.289///290/// \throw system_error If the programming fails.291void292program_new(signals::timer* timer, const datetime::timestamp& now)293{294signals::interrupts_inhibiter inhibiter;295296add_to_all_timers(timer);297reprogram_system_timer(now, inhibiter);298}299300/// Unprograms a timer.301///302/// This removes the timer from the global state and reprograms the global303/// system timer if necessary.304///305/// \param timer The timer to unprogram.306///307/// \return True if the system interval timer has been reprogrammed to308/// another future timer; false if there are no more active timers.309bool310unprogram(signals::timer* timer)311{312signals::interrupts_inhibiter inhibiter;313314LD(F("Unprogramming timer; previously firing on %s") % timer->when());315316remove_from_all_timers(timer);317if (_all_timers.empty()) {318return false;319} else {320reprogram_system_timer(datetime::timestamp::now(), inhibiter);321return true;322}323}324325/// Executes active timers.326///327/// Active timers are all those that fire on or before 'now'.328///329/// \param now The current time.330void331fire(const datetime::timestamp& now)332{333timers_vector to_run;334{335signals::interrupts_inhibiter inhibiter;336to_run = compute_timers_to_run_and_prune_old(now, inhibiter);337reprogram_system_timer(now, inhibiter);338}339340for (timers_vector::iterator iter = to_run.begin();341iter != to_run.end(); ++iter) {342signals::detail::invoke_do_fired(*iter);343}344}345};346347348/// Unique instance of the global state.349static std::unique_ptr< global_state > globals;350351352/// SIGALRM handler for the timer implementation.353///354/// \param signo The signal received; must be SIGALRM.355static void356sigalrm_handler(const int signo)357{358PRE(signo == SIGALRM);359globals->fire(datetime::timestamp::now());360}361362363} // anonymous namespace364365366/// Indirection to invoke the private do_fired() method of a timer.367///368/// \param timer The timer on which to run do_fired().369void370utils::signals::detail::invoke_do_fired(timer* timer)371{372timer->do_fired();373}374375376/// Internal implementation for the timer.377///378/// We assume that there is a 1-1 mapping between timer objects and impl379/// objects. If this assumption breaks, then the rest of the code in this380/// module breaks as well because we use pointers to the parent timer as the381/// identifier of the timer.382struct utils::signals::timer::impl : utils::noncopyable {383/// Timestamp when this timer is expected to fire.384///385/// Note that the timer might be processed after this timestamp, so users of386/// this field need to check for timers that fire on or before the387/// activation time.388datetime::timestamp when;389390/// True until unprogram() is called.391bool programmed;392393/// Whether this timer has fired already or not.394///395/// This is updated from an interrupt context, hence why it is marked396/// volatile.397volatile bool fired;398399/// Constructor.400///401/// \param when_ Timestamp when this timer is expected to fire.402impl(const datetime::timestamp& when_) :403when(when_), programmed(true), fired(false)404{405}406407/// Destructor.408~impl(void) {409}410};411412413/// Constructor; programs a run-once timer.414///415/// This programs the global timer and signal handler if this is the first timer416/// being installed. Otherwise, reprograms the global timer if this timer417/// expires earlier than all other active timers.418///419/// \param delta The time until the timer fires.420signals::timer::timer(const datetime::delta& delta)421{422signals::interrupts_inhibiter inhibiter;423424const datetime::timestamp now = datetime::timestamp::now();425_pimpl.reset(new impl(now + delta));426if (globals.get() == NULL) {427globals.reset(new global_state(this, now));428} else {429globals->program_new(this, now);430}431}432433434/// Destructor; unprograms the timer if still programmed.435///436/// Given that this is a destructor and it can't report errors back to the437/// caller, the caller must attempt to call unprogram() on its own. This is438/// extremely important because, otherwise, expired timers will never run!439signals::timer::~timer(void)440{441signals::interrupts_inhibiter inhibiter;442443if (_pimpl->programmed) {444LW("Auto-destroying still-programmed signals::timer object");445try {446unprogram();447} catch (const system_error& e) {448UNREACHABLE;449}450}451452if (!_pimpl->fired) {453const datetime::timestamp now = datetime::timestamp::now();454if (now > _pimpl->when) {455LW("Expired timer never fired; the code never called unprogram()!");456}457}458}459460461/// Returns the time of the timer activation.462///463/// \return A timestamp that has no relation to the current time (i.e. can be in464/// the future or in the past) nor the timer's activation status.465const datetime::timestamp&466signals::timer::when(void) const467{468return _pimpl->when;469}470471472/// Callback for the SIGALRM handler when this timer expires.473///474/// \warning This is executed from a signal handler context without signals475/// inhibited. See signal(7) for acceptable system calls.476void477signals::timer::do_fired(void)478{479PRE(!_pimpl->fired);480_pimpl->fired = true;481callback();482}483484485/// User-provided callback to run when the timer expires.486///487/// The default callback does nothing. We record the activation of the timer488/// separately, which may be appropriate in the majority of the cases.489///490/// \warning This is executed from a signal handler context without signals491/// inhibited. See signal(7) for acceptable system calls.492void493signals::timer::callback(void)494{495// Intentionally left blank.496}497498499/// Checks whether the timer has fired already or not.500///501/// \return Returns true if the timer has fired.502bool503signals::timer::fired(void) const504{505return _pimpl->fired;506}507508509/// Unprograms the timer.510///511/// \pre The timer is programmed (i.e. this can only be called once).512///513/// \post If the timer never fired asynchronously because the signal delivery514/// did not arrive on time, make sure we invoke the timer's callback here.515///516/// \throw system_error If unprogramming the timer failed.517void518signals::timer::unprogram(void)519{520signals::interrupts_inhibiter inhibiter;521522if (!_pimpl->programmed) {523// We cannot assert that the timer is not programmed because it might524// have been unprogrammed asynchronously between the time we called525// unprogram() and the time we reach this. Simply return in this case.526LD("Called unprogram on already-unprogrammed timer; possibly just "527"a race");528return;529}530531if (!globals->unprogram(this)) {532globals.reset();533}534_pimpl->programmed = false;535536// Handle the case where the timer has expired before we ever got its537// corresponding signal. Do so by invoking its callback now.538if (!_pimpl->fired) {539const datetime::timestamp now = datetime::timestamp::now();540if (now > _pimpl->when) {541LW(F("Firing expired timer on destruction (was to fire on %s)") %542_pimpl->when);543do_fired();544}545}546}547548549