// Copyright 2010 The Kyua Authors. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Google Inc. nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "utils/signals/timer.hpp" extern "C" { #include #include } #include #include #include #include #include "utils/datetime.hpp" #include "utils/format/macros.hpp" #include "utils/logging/macros.hpp" #include "utils/noncopyable.hpp" #include "utils/optional.ipp" #include "utils/sanity.hpp" #include "utils/signals/exceptions.hpp" #include "utils/signals/interrupts.hpp" #include "utils/signals/programmer.hpp" namespace datetime = utils::datetime; namespace signals = utils::signals; using utils::none; using utils::optional; namespace { static void sigalrm_handler(const int); /// Calls setitimer(2) with exception-based error reporting. /// /// This does not currently support intervals. /// /// \param delta The time to the first activation of the programmed timer. /// \param old_timeval If not NULL, pointer to a timeval into which to store the /// existing system timer. /// /// \throw system_error If the call to setitimer(2) fails. static void safe_setitimer(const datetime::delta& delta, itimerval* old_timeval) { ::itimerval timeval; timerclear(&timeval.it_interval); timeval.it_value.tv_sec = delta.seconds; timeval.it_value.tv_usec = delta.useconds; if (::setitimer(ITIMER_REAL, &timeval, old_timeval) == -1) { const int original_errno = errno; throw signals::system_error("Failed to program system's interval timer", original_errno); } } /// Deadline scheduler for all user timers on top of the unique system timer. class global_state : utils::noncopyable { /// Collection of active timers. /// /// Because this is a collection of pointers, all timers are guaranteed to /// be unique, and we want all of these pointers to be valid. typedef std::set< signals::timer* > timers_set; /// Sequence of ordered timers. typedef std::vector< signals::timer* > timers_vector; /// Collection of active timestamps by their activation timestamp. /// /// This collection is ordered intentionally so that it can be scanned /// sequentially to find either expired or expiring-now timers. typedef std::map< datetime::timestamp, timers_set > timers_by_timestamp_map; /// The original timer before any timer was programmed. ::itimerval _old_timeval; /// Programmer for the SIGALRM handler. std::auto_ptr< signals::programmer > _sigalrm_programmer; /// Time of the current activation of the timer. datetime::timestamp _timer_activation; /// Mapping of all active timers using their timestamp as the key. timers_by_timestamp_map _all_timers; /// Adds a timer to the _all_timers map. /// /// \param timer The timer to add. void add_to_all_timers(signals::timer* timer) { timers_set& timers = _all_timers[timer->when()]; INV(timers.find(timer) == timers.end()); timers.insert(timer); } /// Removes a timer from the _all_timers map. /// /// This ensures that empty vectors are removed from _all_timers if the /// removal of the timer causes its bucket to be emptied. /// /// \param timer The timer to remove. void remove_from_all_timers(signals::timer* timer) { // We may not find the timer in _all_timers if the timer has fired, // because fire() took it out from the map. timers_by_timestamp_map::iterator iter = _all_timers.find( timer->when()); if (iter != _all_timers.end()) { timers_set& timers = (*iter).second; INV(timers.find(timer) != timers.end()); timers.erase(timer); if (timers.empty()) { _all_timers.erase(iter); } } } /// Calculates all timers to execute at this timestamp. /// /// \param now The current timestamp. /// /// \post _all_timers is updated to contain only the timers that are /// strictly in the future. /// /// \return A sequence of valid timers that need to be invoked in the order /// of activation. These are all previously registered timers with /// activations in the past. timers_vector compute_timers_to_run_and_prune_old( const datetime::timestamp& now, const signals::interrupts_inhibiter& /* inhibiter */) { timers_vector to_run; timers_by_timestamp_map::iterator iter = _all_timers.begin(); while (iter != _all_timers.end() && (*iter).first <= now) { const timers_set& timers = (*iter).second; to_run.insert(to_run.end(), timers.begin(), timers.end()); // Remove expired entries here so that we can always assume that // the first entry in all_timers corresponds to the next // activation. const timers_by_timestamp_map::iterator previous_iter = iter; ++iter; _all_timers.erase(previous_iter); } return to_run; } /// Adjusts the global system timer to point to the next activation. /// /// \param now The current timestamp. /// /// \throw system_error If the programming fails. void reprogram_system_timer( const datetime::timestamp& now, const signals::interrupts_inhibiter& /* inhibiter */) { if (_all_timers.empty()) { // Nothing to do. We can reach this case if all the existing timers // are in the past and they all fired. Just ignore the request and // leave the global timer as is. return; } // While fire() prunes old entries from the list of timers, it is // possible for this routine to run with "expired" timers (i.e. timers // whose deadline lies in the past but that have not yet fired for // whatever reason that is out of our control) in the list. We have to // iterate until we find the next activation instead of assuming that // the first entry represents the desired value. timers_by_timestamp_map::const_iterator iter = _all_timers.begin(); PRE(!(*iter).second.empty()); datetime::timestamp next = (*iter).first; while (next < now) { ++iter; if (iter == _all_timers.end()) { // Nothing to do. We can reach this case if all the existing // timers are in the past but they have not yet fired. return; } PRE(!(*iter).second.empty()); next = (*iter).first; } if (next < _timer_activation || now > _timer_activation) { INV(next >= now); const datetime::delta delta = next - now; LD(F("Reprogramming timer; firing on %s; now is %s") % next % now); safe_setitimer(delta, NULL); _timer_activation = next; } } public: /// Programs the first timer. /// /// The programming of the first timer involves setting up the SIGALRM /// handler and installing a timer handler for the first time, which in turn /// involves keeping track of the old handlers so that we can restore them. /// /// \param timer The timer being programmed. /// \param now The current timestamp. /// /// \throw system_error If the programming fails. global_state(signals::timer* timer, const datetime::timestamp& now) : _timer_activation(timer->when()) { PRE(now < timer->when()); signals::interrupts_inhibiter inhibiter; const datetime::delta delta = timer->when() - now; LD(F("Installing first timer; firing on %s; now is %s") % timer->when() % now); _sigalrm_programmer.reset( new signals::programmer(SIGALRM, sigalrm_handler)); try { safe_setitimer(delta, &_old_timeval); _timer_activation = timer->when(); add_to_all_timers(timer); } catch (...) { _sigalrm_programmer.reset(NULL); throw; } } /// Unprograms all timers. /// /// This clears the global system timer and unsets the SIGALRM handler. ~global_state(void) { signals::interrupts_inhibiter inhibiter; LD("Unprogramming all timers"); if (::setitimer(ITIMER_REAL, &_old_timeval, NULL) == -1) { UNREACHABLE_MSG("Failed to restore original timer"); } _sigalrm_programmer->unprogram(); _sigalrm_programmer.reset(NULL); } /// Programs a new timer, possibly adjusting the global system timer. /// /// Programming any timer other than the first one only involves reloading /// the existing timer, not backing up the previous handler nor installing a /// handler for SIGALRM. /// /// \param timer The timer being programmed. /// \param now The current timestamp. /// /// \throw system_error If the programming fails. void program_new(signals::timer* timer, const datetime::timestamp& now) { signals::interrupts_inhibiter inhibiter; add_to_all_timers(timer); reprogram_system_timer(now, inhibiter); } /// Unprograms a timer. /// /// This removes the timer from the global state and reprograms the global /// system timer if necessary. /// /// \param timer The timer to unprogram. /// /// \return True if the system interval timer has been reprogrammed to /// another future timer; false if there are no more active timers. bool unprogram(signals::timer* timer) { signals::interrupts_inhibiter inhibiter; LD(F("Unprogramming timer; previously firing on %s") % timer->when()); remove_from_all_timers(timer); if (_all_timers.empty()) { return false; } else { reprogram_system_timer(datetime::timestamp::now(), inhibiter); return true; } } /// Executes active timers. /// /// Active timers are all those that fire on or before 'now'. /// /// \param now The current time. void fire(const datetime::timestamp& now) { timers_vector to_run; { signals::interrupts_inhibiter inhibiter; to_run = compute_timers_to_run_and_prune_old(now, inhibiter); reprogram_system_timer(now, inhibiter); } for (timers_vector::iterator iter = to_run.begin(); iter != to_run.end(); ++iter) { signals::detail::invoke_do_fired(*iter); } } }; /// Unique instance of the global state. static std::auto_ptr< global_state > globals; /// SIGALRM handler for the timer implementation. /// /// \param signo The signal received; must be SIGALRM. static void sigalrm_handler(const int signo) { PRE(signo == SIGALRM); globals->fire(datetime::timestamp::now()); } } // anonymous namespace /// Indirection to invoke the private do_fired() method of a timer. /// /// \param timer The timer on which to run do_fired(). void utils::signals::detail::invoke_do_fired(timer* timer) { timer->do_fired(); } /// Internal implementation for the timer. /// /// We assume that there is a 1-1 mapping between timer objects and impl /// objects. If this assumption breaks, then the rest of the code in this /// module breaks as well because we use pointers to the parent timer as the /// identifier of the timer. struct utils::signals::timer::impl : utils::noncopyable { /// Timestamp when this timer is expected to fire. /// /// Note that the timer might be processed after this timestamp, so users of /// this field need to check for timers that fire on or before the /// activation time. datetime::timestamp when; /// True until unprogram() is called. bool programmed; /// Whether this timer has fired already or not. /// /// This is updated from an interrupt context, hence why it is marked /// volatile. volatile bool fired; /// Constructor. /// /// \param when_ Timestamp when this timer is expected to fire. impl(const datetime::timestamp& when_) : when(when_), programmed(true), fired(false) { } /// Destructor. ~impl(void) { } }; /// Constructor; programs a run-once timer. /// /// This programs the global timer and signal handler if this is the first timer /// being installed. Otherwise, reprograms the global timer if this timer /// expires earlier than all other active timers. /// /// \param delta The time until the timer fires. signals::timer::timer(const datetime::delta& delta) { signals::interrupts_inhibiter inhibiter; const datetime::timestamp now = datetime::timestamp::now(); _pimpl.reset(new impl(now + delta)); if (globals.get() == NULL) { globals.reset(new global_state(this, now)); } else { globals->program_new(this, now); } } /// Destructor; unprograms the timer if still programmed. /// /// Given that this is a destructor and it can't report errors back to the /// caller, the caller must attempt to call unprogram() on its own. This is /// extremely important because, otherwise, expired timers will never run! signals::timer::~timer(void) { signals::interrupts_inhibiter inhibiter; if (_pimpl->programmed) { LW("Auto-destroying still-programmed signals::timer object"); try { unprogram(); } catch (const system_error& e) { UNREACHABLE; } } if (!_pimpl->fired) { const datetime::timestamp now = datetime::timestamp::now(); if (now > _pimpl->when) { LW("Expired timer never fired; the code never called unprogram()!"); } } } /// Returns the time of the timer activation. /// /// \return A timestamp that has no relation to the current time (i.e. can be in /// the future or in the past) nor the timer's activation status. const datetime::timestamp& signals::timer::when(void) const { return _pimpl->when; } /// Callback for the SIGALRM handler when this timer expires. /// /// \warning This is executed from a signal handler context without signals /// inhibited. See signal(7) for acceptable system calls. void signals::timer::do_fired(void) { PRE(!_pimpl->fired); _pimpl->fired = true; callback(); } /// User-provided callback to run when the timer expires. /// /// The default callback does nothing. We record the activation of the timer /// separately, which may be appropriate in the majority of the cases. /// /// \warning This is executed from a signal handler context without signals /// inhibited. See signal(7) for acceptable system calls. void signals::timer::callback(void) { // Intentionally left blank. } /// Checks whether the timer has fired already or not. /// /// \return Returns true if the timer has fired. bool signals::timer::fired(void) const { return _pimpl->fired; } /// Unprograms the timer. /// /// \pre The timer is programmed (i.e. this can only be called once). /// /// \post If the timer never fired asynchronously because the signal delivery /// did not arrive on time, make sure we invoke the timer's callback here. /// /// \throw system_error If unprogramming the timer failed. void signals::timer::unprogram(void) { signals::interrupts_inhibiter inhibiter; if (!_pimpl->programmed) { // We cannot assert that the timer is not programmed because it might // have been unprogrammed asynchronously between the time we called // unprogram() and the time we reach this. Simply return in this case. LD("Called unprogram on already-unprogrammed timer; possibly just " "a race"); return; } if (!globals->unprogram(this)) { globals.reset(NULL); } _pimpl->programmed = false; // Handle the case where the timer has expired before we ever got its // corresponding signal. Do so by invoking its callback now. if (!_pimpl->fired) { const datetime::timestamp now = datetime::timestamp::now(); if (now > _pimpl->when) { LW(F("Firing expired timer on destruction (was to fire on %s)") % _pimpl->when); do_fired(); } } }