// -*- c-basic-offset: 4; related-file-name: "../../lib/gaprate.cc" -*- #ifndef CLICK_GAPRATE_HH #define CLICK_GAPRATE_HH #include CLICK_DECLS class ErrorHandler; /** @file * @brief A Click helper class for implementing a uniform rate. */ /** @class GapRate include/click/gaprate.hh * @brief A helper class for implementing a uniform rate. * * The GapRate class helps its user implement a process with a uniform rate: * a process in which one user event happens every T seconds. GapRate is * designed to efficiently model high rates. (Contrast this with Timer, * which can serve a similar function at low rates.) * * GapRate is not a great choice for limiting the rates of external * processes. See TokenBucketX, a token bucket rate limiter. * * GapRate models an underlying "true" rated process with the correct rate. * It also keeps a counter, maintained by its user via the update() method, * that measures the progress of the user's rated process. The * needs_update() method compares this counter with its expected value, which * is determined by the true rated process. If the user's rated process is * running behind, then needs_update() returns true; the user should trigger * an event and call the update() method. If the user's rated process is * just right or is running faster than the true process, then needs_update() * returns false; the user should not trigger an event. * * Creating a non-bursty rate can be expensive and difficult. GapRate * attempts to create a non-bursty rate using timestamps and some interesting * microsecond-based arithmetic. External factors can cause scheduling * hiccups where GapRate is not called as much as expected. GapRate * compensates for hiccups: the user's rated process may fall up to a full * second behind the true rated process, then catch up in a burst. More than * one second's worth of lag is ignored. * * The maximum rate GapRate can implement is MAX_RATE events per second. * * @sa TokenBucketX, Timer */ class GapRate { public: /** @brief Construct a GapRate object with initial rate 0. */ inline GapRate(); /** @brief Construct a GapRate object with initial rate @a r. * @param r initial rate (events per second) */ inline GapRate(unsigned r); /** @brief Return the current rate. */ inline unsigned rate() const; /** @brief Set the current rate to @a r. * @param r desired rate (events per second) * * Rates larger than MAX_RATE are reduced to MAX_RATE. Also performs the * equivalent of a reset() to flush old state. */ inline void set_rate(unsigned r); /** @brief Set the current rate to @a r. * @param r desired rate (events per second) * @param errh error handler * * Acts like set_rate(@a r), except that an warning is reported to @a errh * if @a r is larger than MAX_RATE. */ void set_rate(unsigned r, ErrorHandler *errh); /** @brief Returns whether the user's rate is behind the true rate. * @param ts current timestamp * * Returns true iff the user's rate is currently behind the true rate, * meaning the user should cause an event and call update(). */ inline bool need_update(const Timestamp &ts); /** @brief Returns a time when the user's rate will be behind the true rate. * @pre need_update() has been called at least once. * @return If the rate is 0, or need_update() has not been called, * returns Timestamp(). If the user's rate is already behind the true * rate, returns a time no greater than the argument passed to the last * need_update(). Otherwise, returns a time in the future when * need_update() will return true. */ inline Timestamp expiry() const; /** @brief Increment the user event counter. * * Call this function when causing a user event. */ inline void update(); /** @brief Increment the user event counter by @a delta. * @param delta number of user events * * @note This may be faster than calling update() @a delta times. * Furthermore, @a delta can be negative. */ inline void update_with(int delta); /** @brief Resets the true rate counter. * * This function flushes any old information about the true rate counter * and its relationship to the user's rate counter. */ inline void reset(); enum { UGAP_SHIFT = 12 }; enum { MAX_RATE = 1000000U << UGAP_SHIFT }; private: unsigned _ugap; // (1000000 << UGAP_SHIFT) / _rate int _sec_count; // number of updates this second so far Timestamp::seconds_type _tv_sec; // current second unsigned _rate; // desired rate #if DEBUG_GAPRATE Timestamp _last; #endif inline void initialize_rate(unsigned rate); }; /** @brief Reset the underlying rated process. */ inline void GapRate::reset() { _tv_sec = -1; #if DEBUG_GAPRATE _last.set_sec(0); #endif } inline void GapRate::initialize_rate(unsigned r) { _rate = r; _ugap = (r == 0 ? MAX_RATE + 1 : MAX_RATE / r); #if DEBUG_GAPRATE click_chatter("ugap: %u", _ugap); #endif } inline void GapRate::set_rate(unsigned r) { if (r > MAX_RATE) r = MAX_RATE; if (_rate != r) { initialize_rate(r); if (_tv_sec >= 0 && r != 0) { Timestamp now = Timestamp::now(); _sec_count = (now.usec() << UGAP_SHIFT) / _ugap; } } } inline GapRate::GapRate() { initialize_rate(0); reset(); } inline GapRate::GapRate(unsigned r) : _rate(0) { initialize_rate(r); reset(); } inline unsigned GapRate::rate() const { return _rate; } inline bool GapRate::need_update(const Timestamp &now) { // this is an approximation of: // unsigned need = (unsigned) ((now.usec() / 1000000.0) * _rate) unsigned need = (now.usec() << UGAP_SHIFT) / _ugap; if (_tv_sec < 0) { // 27.Feb.2005: often OK to send a packet after reset unless rate is // 0 -- requested by Bart Braem // check include/click/gaprate.hh (1.2) _tv_sec = now.sec(); _sec_count = need + ((now.usec() << UGAP_SHIFT) - (need * _ugap) > _ugap / 2); } else if (now.sec() > _tv_sec) { _tv_sec = now.sec(); if (_sec_count > 0) _sec_count -= _rate; } #if DEBUG_GAPRATE click_chatter("%p{timestamp} -> %u @ %u [%d]", &now, need, _sec_count, (int)need >= _sec_count); #endif return ((int)need >= _sec_count); } inline void GapRate::update() { _sec_count++; } inline void GapRate::update_with(int delta) { _sec_count += delta; } inline Timestamp GapRate::expiry() const { if (_tv_sec < 0 || _rate == 0) return Timestamp(); else if (_sec_count < 0) return Timestamp(_tv_sec, 0); else { Timestamp::seconds_type sec = _tv_sec; int count = _sec_count; if ((unsigned) count >= _rate) { sec += count / _rate; count = count % _rate; } // uint32_t usec = (int) (count * 1000000.0 / _rate) uint32_t usec = (count * _ugap) >> UGAP_SHIFT; return Timestamp::make_usec(sec, usec); } } CLICK_ENDDECLS #endif