// balb_leakybucket.h -*-C++-*- #ifndef INCLUDED_BALB_LEAKYBUCKET #define INCLUDED_BALB_LEAKYBUCKET #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a mechanism to monitor the consumption rate of a resource. // //@CLASSES: // balb::LeakyBucket: a leaky bucket rate monitor // //@SEE_ALSO: balb_ratelimiter // //@DESCRIPTION This component provides a mechanism, 'balb::LeakyBucket', that // implements a leaky bucket algorithm that allows clients to monitor whether a // resource is being consumed at a particular rate. // // The name of this mechanism, leaky bucket, derives from an analogy of pouring // water into a bucket with a hole at the bottom. The maximum rate at which // water will drain out the bucket depends on the size of the hole, and not on // the rate at which water is poured into the bucket. If more water is being // poured into the bucket than being drained, the bucket will eventually // overflow. If the person pouring water into a leaky bucket ensures the // bucket doesn't overflow, then the average rate they pour water will, over // time, be limited by the rate at which water flows out of the bucket. By // analogy, a leaky bucket provides a means to limit the rate of consumption of // some resource (water poured into the bucket) to a configured rate (the size // of the hole in the bucket). // // The behavior of a leaky bucket is determined by two properties: the capacity // and the drain rate. The drain rate, measured in 'units/s', is the rate at // which the resource is drained. The capacity, measured in 'units', is the // maximum amount of the resource that the leaky bucket can hold before it // overflows. 'unit' is a generic unit of measurement (e.g., bytes, number of // messages, packets, liters, clock cycles, etc.). Note that the drain rate // determines average rate of resource consumption, while the capacity // restricts the time period over which the average actual rate of resource // consumption approaches the drain rate. // ///Adding Units ///------------ // Units can be added to a leaky bucket by either submitting them or reserving // them. Submitted units are removed from a leaky bucket at the drain rate, // while reserved units remain unchanged until they are later either cancelled // (removed from the leaky bucket) or submitted. // ///Submitting Units /// - - - - - - - - // Units can be submitted to a leaky bucket by invoking the 'submit' method, // and should be added only after the resource had been consumed. // // Figure 1 illustrates a typical workflow for submitting units to a leaky // bucket. //.. // Fig. 1: Capacity = 5 units, Rate = 1 unit / second // // Submit 5 Submit 2 // // 7| | 7| | 7| | 7| | // 6| | 6| | 6| | 6| | // c--5|~~~~~| c--5|-----| c--5|-----| c--5|-----| // 4|~~~~~| 4| | 4| | 4| | // 3|~~~~~| 3| | 3|~~~~~| 3| | // 2|~~~~~| 2| | 2|~~~~~| 2| | // 1|~~~~~| 1|~~~~~| 1|~~~~~| 1| | // +-- --+ +-- --+ +-- --+ +-- --+ // // Time: t0 t0 + 4s t0 + 4s t0 + 10s //.. // Suppose that we have an empty leaky bucket with a capacity of 'c = 5 units' // and a drain rate of 'd = 1 units/s'. At 't0', we submit 5 units to the // leaky bucket, bringing the total number of units held up to 5. At 't0 + // 4s', 4 units have been drained from the leaky bucket, bringing the number of // units held down to 1. Finally, at 't0 + 10s', all units have been drained // from the leaky bucket, making it empty. // // Unlike a real-life water bucket, units submitted to a leaky bucket don't // spillover after its capacity has been exceeded, instead the leaky bucket may // hold a number of units beyond its capacity, as examined in Figure 2 below., // these units are still contained in the leaky bucket. // // Figure 2 illustrates what happens if a leaky bucket exceeds its capacity. // This scenario is the same as that in Figure 1, but at time 't0 + 4s', we // submit 6 units instead of 2. //.. // Fig. 2: Capacity = 5 units, Rate = 1 unit / second // // Submit 5 Submit 6 // // 7| | 7| | 7|~~~~~| 7| | // 6| | 6| | 6|~~~~~| 6| | // c--5|~~~~~| c--5|-----| c--5|~~~~~| c--5|-----| // 4|~~~~~| 4| | 4|~~~~~| 4| | // 3|~~~~~| 3| | 3|~~~~~| 3| | // 2|~~~~~| 2| | 2|~~~~~| 2| | // 1|~~~~~| 1|~~~~~| 1|~~~~~| 1|~~~~~| // +-- --+ +-- --+ +-- --+ +-- --+ // // Time: t0 t0 + 4s t0 + 4s t0 + 10s //.. // At 't0 + 4s', when the number of units held by the leaky bucket is 1, we // submit 6 more units. This brings the number of units held to 7, which // exceeds the capacity of the leaky bucket. At 't0 + 10s', 6 units had been // drained from the leaky bucket bring the number of units held down to 1. // ///Reserving Units ///- - - - - - - - // Units can be reserved for a leaky bucket using the 'reserve' method, and // they may be later canceled using the 'cancelReserved' method or submitted // using the 'submitReserved' method. Unlike submitted units, reserved units // do *not* drain from the leaky bucket; like submitted units, reserved units // count toward the total number of units for the purposes of determining // whether a leaky bucket has exceeded its capacity. Reserving units // effectively decreases the capacity of a leaky bucket. Therefore, the time // interval between reserving units and submitting or canceling the reservation // should be kept as short as possible. For a practical example of using // reserved units, please see 'balb_reservationguard'. // // Figure 3 illustrate an example of how reserving units works in a leaky // bucket. //.. // Fig. 3: Capacity = 5 units, Rate = 1 unit / second // // Reserve 4 Submit 3 Cancel 1 // from reserve from reserve // // 7| | 7| | 7| | 7| | 7| | // 6| | 6| | 6| | 6| | 6| | // c--5|-----| c--5|-----| c--5|-----| c--5|-----| c--5|-----| // 4|#####| 4|#####| 4|~~~~~| 4| | 4| | // 3|#####| 3|#####| 3|~~~~~| 3| | 3| | // 2|#####| 2|#####| 2|~~~~~| 2| | 2| | // 1|#####| 1|#####| 1|#####| 1|#####| 1| | // +-- --+ +-- --+ +-- --+ +-- --+ +-- --+ // // Time: t0 t0 + 5s t0 + 6s t0 + 9s t0 + 10s //.. // Suppose that we have an empty leaky bucket with a capacity of 'c = 5 units' // and a drain rate of 'd = 1 units/s'. At 't0' we reserve 4 units. At 't0 + // 5s', we observe that none of the reserved units are drained from the leaky // bucket. At 't0 + 6s', we submit 3 of the previously reserved units, which // brings the number of reserved units down to 1 and the number of units held // up to 3. At 't0 + 9s', we observe that all but the remaining reserved unit // have been drained from the bucket. Finally, at 't0 + 10s', we cancel the // remaining reserved unit. // ///Monitoring Resource Usage ///------------------------- // The recommended usage of a leaky bucket is to first check whether 1 unit can // be added without causing the leaky bucket to overflow, and if so, consume // the desired amount of the resource. Afterwards, submit the amount of // consumed resource to the leaky bucket. // ///Checking for Overflow ///- - - - - - - - - - - // A leaky bucket can be queried whether submitting a 1 more unit would cause // it to overflow via the 'wouldOverflow' method. This method facilitates the // recommended usage of a leak bucket: check whether 1 more unit can be added // without causing the leaky bucket to overflow, and if so, consume the desired // amount of the resource (which may be more than 1). Compared to the // alternative -- checking whether the desired amount can be submitted without // overflow -- this recommended usage may allow a limited spike in the rate of // actual consumption when the leaky bucket is empty (which is often // acceptable) but is able to sustain a long term average that is actually // closer to the drain rate. // ///Modeling a Network Connection ///----------------------------- // The primary use case of leaky bucket is limiting the rate at which data is // written on a network. In this use case, the drain rate of the bucket // corresponds to the *ideal* maximum transmission rate that the client wishes // to enforce on their outgoing connection. Clients may choose to provide a // value related to the physical limitations of their network or any other // arbitrary limit. The function of a leaky bucket's capacity is to limit the // time period over which the average actual transmission rate may exceed the // configured drain rate of the leaky bucket (see 'Approximations' section and // 'Sliding Time-Window' section). // ///Approximations ///-------------- // Leaky bucket is modeled on a water bucket with a hole, but as a leaky bucket // does not manage any resources, there are several approximations to this // model: // //: 1 Units are submitted instantaneously to the leaky bucket, whereas the //: consumption of a resource occurs over time at a rate that depends on the //: nature and speed of the resource. //: //: 2 Leaky bucket simulates the consumption of a resource with a specified //: fixed drain rate, but the resource is actually consumed at different //: rates over time. This approximation still guarantees that the actual //: consumption rate does not exceed the specified drain rate when amortized //: over some configured period of time (determined by the capacity and the //: drain rate of the bucket), but does not prevent the consumption rate from //: spiking above the drain rate for shorter periods of time (see 'Sliding //: Time-Window' section). // ///Sliding Time-Window ///------------------- // One of the properties of the resource pattern created by using a leaky // bucket is an approximation of a sliding time window over which the average // consumption rate is guaranteed to be less than the drain rate. This time // period can be calculated using the leaky bucket's capacity and drain rate, // which can be conveniently performed using the 'calculateTimeWindow' class // method. // ///Time Synchronization ///-------------------- // Leaky bucket does not utilize an internal timer, so timing must be handled // manually. Clients can specify an initial time interval for the leaky bucket // at construction or using the 'reset' method. Whenever the number of units // in a leaky bucket needs to be updated, clients must invoke the 'updateState' // method specifying the current time interval. Since leaky bucket cares only // about the elapsed time (not absolute time), the specified time intervals may // be relative to any arbitrary time origin, though all of them must refer to // the same origin. For the sake of consistency, clients are encouraged to use // the unix epoch time (such as the values returned by // 'bdlt::CurrentTime::now'). // ///Usage ///----- // This section illustrates the intended use of this component. // ///Example 1: Controlling Network Traffic Generation ///------------------------------------------------- // In some systems, data is processed faster than they are consumed by I/O // interfaces. This could lead to data loss due to the overflowing of the // buffers where data is queued before being processed. In other systems, // generic resources are shared, and their consumption might need to be managed // in order to guarantee quality-of-service (QOS). // // Suppose we have a network interface capable of transferring at a rate of // 1024 byte/s and an application wants to transmit 5 KB (5120 bytes) of data // over that network in 20 different 256-bytes data chunks. We want to send // data over this interface and want to ensure the transmission uses on average // less than 50% of the available bandwidth, or 512 byte/s. In this way, other // clients can still reasonably send and receive data using the same network // interface. // // Further suppose that we have a function, 'sendData', that transmits a // specified data buffer over that network interface: //.. // bool sendData(const char *buffer, size_t dataSize); // // Send the specified 'buffer' of the specified size 'dataSize' through // // the network interface. Return 'true' if data was sent successfully, // // and 'false' otherwise. //.. // First, we create a leaky bucket having a drain rate of 512 bytes/s, a // capacity of 2560 bytes, and a time origin set to the current time (as an // interval from unix epoch). Note that 'unit', the unit of measurement for // leaky bucket, corresponds to 'byte' in this example: //.. // bsls::Types::Uint64 rate = 512; // bytes/second // bsls::Types::Uint64 capacity = 2560; // bytes // bsls::TimeInterval now = bdlt::CurrentTime::now(); // LeakyBucket bucket(rate, capacity, now); //.. // Then, we define a data buffer to be sent, the size of each data chunk, and // the total size of the data to transmit: //.. // char buffer[5120]; // unsigned int chunkSize = 256; // in bytes // bsls::Types::Uint64 totalSize = 20 * chunkSize; // in bytes // bsls::Types::Uint64 dataSent = 0; // in bytes // // // Load 'buffer'. // // ... //.. // Notice that, for the sake of brevity, we elide the loading of 'buffer' with // the data to be sent. // // Now, we send the chunks of data using a loop. For each iteration, we check // whether submitting another byte would cause the leaky bucket to overflow. // If not, we send an additional chunk of data and submit the number of bytes // sent to the leaky bucket. Note that 'submit' is invoked only after the data // has been sent. //.. // char *data = buffer; // while (dataSent < totalSize) { // now = bdlt::CurrentTime::now(); // if (!bucket.wouldOverflow(now)) { // if (true == sendData(data, chunkSize)) { // data += chunkSize; // bucket.submit(chunkSize); // dataSent += chunkSize; // } // } //.. // Finally, if submitting another byte will cause the leaky bucket to overflow, // then we wait until the submission will be allowed by waiting for an amount // time returned by the 'calculateTimeToSubmit' method: //.. // else { // bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit( // now); // // // Round up the number of microseconds. // bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + // ((timeToSubmit.nanoseconds() % 1000) ? 1 : 0); // bslmt::ThreadUtil::microSleep(static_cast<int>(uS)); // } // } //.. // Notice that we wait by putting the thread into a sleep state instead of // using busy-waiting to better optimize for multi-threaded applications. #include <balscm_version.h> #include <bsls_assert.h> #include <bsls_timeinterval.h> #include <bsls_types.h> #include <bsl_climits.h> namespace BloombergLP { namespace balb { //================== // class LeakyBucket //================== class LeakyBucket { // This mechanism implements a leaky bucket that allows clients to monitor // whether a resource is being consumed at a particular rate. The behavior // of a leak bucket is determined by two properties: the drain rate (in // units/s) and capacity (in units), both of which can be specified at // construction or using the 'setRateAndCapacity' method. // // Units can be added to a leaky bucket by either submitting them using the // 'submit' method or reserving them using the 'reserve' method. Submitted // units are removed from a leaky bucket at the drain rate, while reserved // units stays unaffected in a leaky bucket until they are either cancelled // (removed from the leaky bucket) using the 'cancelReserved' method or // submitted using the 'submitReserved' method. // // Adding units to a leaky bucket will cause it to overflow if after the // units are added, the total number of units in the leaky bucket // (including both submitted and reserved units) exceeds its capacity. A // leaky bucket can be queried whether adding a specified number of units // would cause it to overflow via the 'wouldOverflow' method. If // submitting units to a leaky bucket will cause it to overflow, the // estimated amount of time to wait before 1 more units can be submitted // without causing the leaky bucket to overflow can be determined using the // 'calculateTimeToSubmit' method. // // The state of a leaky bucket must be updated manually using the // 'updateState' method supplying the current time interval. The time // intervals supplied should all refer to the same time origin. // // A leaky bucket keeps some statistics, including the number of submitted // units, that can be accessed using the 'getStatistics' method and reset // using the 'resetStatistics' method. // // The class invariants are: //: o 'capacity() > 0' //: o 'drainRate() > 0' // // This class: //: o is *exception* *neutral* (agnostic) //: o is *const* *thread-safe* // For terminology see 'bsldoc_glossary'. // DATA bsls::Types::Uint64 d_drainRate; // drain rate in units per // second bsls::Types::Uint64 d_capacity; // the bucket capacity in units bsls::Types::Uint64 d_unitsReserved; // reserved units bsls::Types::Uint64 d_unitsInBucket; // number of units currently in // the bucket bsls::Types::Uint64 d_fractionalUnitDrainedInNanoUnits; // fractional number of units // that is carried from the // last drain operation bsls::TimeInterval d_lastUpdateTime; // time of last drain, updated // via the 'updateState' method bsls::TimeInterval d_maxUpdateInterval; // time to drain maximum number // of units bsls::Types::Uint64 d_statSubmittedUnits; // submitted unit counter, // number of submitted units // since last reset bsls::Types::Uint64 d_statSubmittedUnitsAtLastUpdate; // submitted unit counter saved // during last update bsls::TimeInterval d_statisticsCollectionStartTime; // start time for the submitted // unit counter private: // NOT IMPLEMENTED LeakyBucket& operator=(const LeakyBucket&); LeakyBucket(const LeakyBucket&); public: // CLASS METHODS static bsls::Types::Uint64 calculateCapacity( bsls::Types::Uint64 drainRate, const bsls::TimeInterval& timeWindow); // Return the capacity of a leaky bucket as the rounded-down product of // the specified 'drainRate' by the specified 'timeWindow'. If the // result evaluates to 0, return 1. The behavior is undefined unless // the product of 'drainRate' and 'timeWindow' can be represented by a // 64-bit unsigned integral type. static bsls::TimeInterval calculateDrainTime(bsls::Types::Uint64 numUnits, bsls::Types::Uint64 drainRate, bool ceilFlag); // Return the time interval required to drain the specified 'numUnits' // at the specified 'drainRate', round up the number of nanoseconds in // the time interval if the specified 'ceilFlag' is set to 'true', // otherwise, round down the number of nanoseconds. The behavior is // undefined unless the number of seconds in the calculated interval // may be represented by a 64-bit signed integral type. static bsls::TimeInterval calculateTimeWindow( bsls::Types::Uint64 drainRate, bsls::Types::Uint64 capacity); // Return the time interval over which a leaky bucket *approximates* a // moving-total of submitted units, as the rounded-down ratio between // the specified 'capacity' and the specified 'drainRate'. If the // rounded ratio is 0, return a time interval of 1 nanosecond. The // behavior is undefined unless 'drainRate > 0' and // 'capacity / drainRate' can be represented with 64-bit signed // integral type. // CREATORS LeakyBucket(bsls::Types::Uint64 drainRate, bsls::Types::Uint64 capacity, const bsls::TimeInterval& currentTime); // Create an empty leaky bucket having the specified 'drainRate', the // specified 'capacity', and the specified 'currentTime' as the initial // 'lastUpdateTime'. The behavior is undefined unless '0 < newRate', // '0 < newCapacity', and 'LLONG_MIN != currentTime.seconds()'. ~LeakyBucket(); // Destroy this object. // MANIPULATORS bsls::TimeInterval calculateTimeToSubmit( const bsls::TimeInterval& currentTime); // If 1 more unit can be submitted to this leaky bucket without causing // it to overflow, then return a time interval of 0 immediately. // Otherwise, first update the state of this leaky bucket to the // specified 'currentTime'. Then, return the estimated time interval // that should pass from 'currentTime' until 1 more unit can be // submitted to this leaky bucket without causing it to overflow. The // number of nanoseconds in the returned time interval is rounded up. // Note that a time interval of 0 can still be return after the state // of this leaky bucket has been updated to 'currentTime'. Also note // that after waiting for the returned time interval, clients should // typically check again using this method, because additional units // may have been submitted in the interim. The behavior is undefined // unless 'LLONG_MIN != currentTime.seconds()' and the total number of // seconds in the time interval resulting from // 'currentTime - lastUpdateTime()' can be represented with a 64-bit // signed integer. void cancelReserved(bsls::Types::Uint64 numUnits); // Cancel the specified 'numUnits' that were previously reserved. This // method reduces the number of reserved units by 'numUnits'. The // behavior is undefined unless 'numUnits <= unitsReserved()'. void reserve(bsls::Types::Uint64 numUnits); // Reserve the specified 'numUnits' for future use by this leaky // bucket. The behavior is undefined unless 'unitsReserved() + // unitsInBucket() + numOfUnits' can be represented by a 64-bit // unsigned integral type. Note that after this operation, this bucket // may overflow. Also note that the time interval between the // invocations of 'reserve' and 'submitReserved' or 'cancelReserved' // should be kept as short as possible; otherwise, the precision of the // time interval calculated by 'calculateTimeToSubmit' may be // negatively affected. void reset(const bsls::TimeInterval& currentTime); // Reset the following statistic counters for this leaky bucket to 0: // 'unitsInBucket', 'unitsReserved', 'submittedUnits', and // 'unusedUnits'. Set the 'lastUpdateTime' and the // 'statisticCollectionStartTime' to the specified 'currentTime' of // this leaky bucket. The behavior is undefined unless 'LLONG_MIN != // currentTime.seconds()'. void resetStatistics(); // Reset the statics collected for this leaky bucket by setting the // number of units used and the number of units submitted to 0, and set // the 'statisticsCollectionStartTime' to the 'lastUpdateTime' of this // leaky bucket. void setRateAndCapacity(bsls::Types::Uint64 newRate, bsls::Types::Uint64 newCapacity); // Set the drain rate of this leaky bucket to the specified 'newRate' // and the capacity of this leaky bucket to the specified // 'newCapacity'. The behavior is undefined unless '0 < newRate' and // '0 < newCapacity'. void submit(bsls::Types::Uint64 numUnits); // Submit the specified 'numUnits' to this leaky bucket. The behavior // is undefined unless 'unitsReserved() + unitsInBucket() + numUnits' // can be represented by a 64-bit unsigned integral type. Note that // after this operation, this leaky bucket may overflow. void submitReserved(bsls::Types::Uint64 numUnits); // Submit the specified 'numUnits' that were previously reserved. This // method reduces the number of reserved units by 'numUnits' and // submits 'numUnits' to this leaky bucket. The behavior is undefined // unless 'numUnits <= unitsReserved()'. void updateState(const bsls::TimeInterval& currentTime); // Set the 'lastUpdateTime' of this leaky bucket to the specified // 'currentTime'. If 'currentTime' is after 'lastUpdateTime', then // update the 'unitsInBucket' of this leaky bucket by subtracting from // it the number of units drained from 'lastUpdateTime' to // 'currentTime'. If 'currentTime' is before the // 'statisticsCollectionStartTime' of this leaky bucket, set // 'statisticsCollectionStartTime' to 'currentTime'. The behavior is // undefined unless 'LLONG_MIN != currentTime.seconds()' and the total // number of seconds in the time interval resulting from // 'currentTime - lastUpdateTime()' can be represented with a 64-bit // signed integer. bool wouldOverflow(const bsls::TimeInterval& currentTime); // Update the state of this this leaky bucket to the specified // 'currentTime', and return 'true' if adding 1 more unit to this leaky // bucket would cause the total number of units held by this leaky // bucket to exceed its capacity, and 'false' otherwise. Note that // this method counts both submitted units and reserved units toward // the total number of units held by this leaky bucket. The behavior // is undefined unless 'LLONG_MIN != currentTime.seconds()' and the // total number of seconds in the time interval resulting from // 'currentTime - lastUpdateTime()' can be represented with a 64-bit // signed integer. // ACCESSORS bsls::Types::Uint64 capacity() const; // Return the capacity of this leaky bucket. bsls::Types::Uint64 drainRate() const; // Return the drain rate of this leaky bucket. void getStatistics(bsls::Types::Uint64* submittedUnits, bsls::Types::Uint64* unusedUnits) const; // Load, into the specified 'submittedUnits' and the specified // 'unusedUnits' respectively, the numbers of submitted units and the // number of unused units for this leaky bucket from the // 'statisticsCollectionStartTime' to the 'lastUpdateTime'. The number // of unused units is the difference between the number of units that // could have been consumed and the number of units actually submitted // for the time period. bsls::TimeInterval lastUpdateTime() const; // Return the time interval when this leaky bucket was last updated. bsls::TimeInterval statisticsCollectionStartTime() const; // Return the time interval when the collection of the statistics (as // returned by 'getStatistics') started. bsls::Types::Uint64 unitsInBucket() const; // Return the number of submitted units in this leaky bucket. bsls::Types::Uint64 unitsReserved() const; // Return the number of reserved units in this leaky bucket. }; // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ //----------------------- // class LeakyBucket //----------------------- // CREATORS inline LeakyBucket::~LeakyBucket() { BSLS_ASSERT_SAFE(0 < d_drainRate); BSLS_ASSERT_SAFE(0 < d_capacity); } // MANIPULATORS inline void LeakyBucket::cancelReserved(bsls::Types::Uint64 numUnits) { BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); if (numUnits > d_unitsReserved) { d_unitsReserved = 0; } else { d_unitsReserved -= numUnits; } } inline void LeakyBucket::reserve(bsls::Types::Uint64 numUnits) { // Check whether adding 'numUnits' causes an unsigned 64-bit integral type // to overflow. BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsReserved); BSLS_ASSERT_SAFE( d_unitsInBucket <= ULLONG_MAX - d_unitsReserved - numUnits); d_unitsReserved += numUnits; } inline void LeakyBucket::reset(const bsls::TimeInterval& currentTime) { BSLS_ASSERT_SAFE(LLONG_MIN != currentTime.seconds()); d_lastUpdateTime = currentTime; d_unitsInBucket = 0; d_unitsReserved = 0; resetStatistics(); } inline void LeakyBucket::resetStatistics() { d_statisticsCollectionStartTime = d_lastUpdateTime; d_statSubmittedUnits = 0; d_statSubmittedUnitsAtLastUpdate = 0; } inline void LeakyBucket::submit(bsls::Types::Uint64 numUnits) { // Check whether adding 'numUnits' causes an unsigned 64-bit integer type // to overflow. BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsInBucket); BSLS_ASSERT_SAFE( d_unitsReserved <= ULLONG_MAX - d_unitsInBucket - numUnits); d_unitsInBucket += numUnits; d_statSubmittedUnits += numUnits; } inline void LeakyBucket::submitReserved(bsls::Types::Uint64 numUnits) { BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); d_unitsReserved -= numUnits; submit(numUnits); } // ACCESSORS inline bsls::Types::Uint64 LeakyBucket::capacity() const { return d_capacity; } inline bsls::Types::Uint64 LeakyBucket::drainRate() const { return d_drainRate; } inline bsls::TimeInterval LeakyBucket::lastUpdateTime() const { return d_lastUpdateTime; } inline bsls::TimeInterval LeakyBucket::statisticsCollectionStartTime() const { return d_statisticsCollectionStartTime; } inline bsls::Types::Uint64 LeakyBucket::unitsInBucket() const { return d_unitsInBucket; } inline bsls::Types::Uint64 LeakyBucket::unitsReserved() const { return d_unitsReserved; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2021 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------