BDE 4.14.0 Production release
Loading...
Searching...
No Matches
balb_reservationguard

Detailed Description

Outline

Purpose

Provide a generic proctor for rate controlling objects.

Classes

See also
balb_leakybucket, balb_ratelimiter

Description

This component provides generic proctor to automatically reserve and release units from a rate controlling object. The rate controlling object can be of any type (typically either a balb::RateLimiter or balb::LeakyBucket) that provides the following methods:

void reserve(bsls::Types::Uint64 numOfUnits);
void submitReserved(bsls::Types::Uint64 numOfUnits);
void cancelReserved(bsls::Types::Uint64 numOfUnits);
unsigned long long Uint64
Definition bsls_types.h:137

Use balb::ReservationGuard to ensure that reserved units will be correctly returned to a rate controlling object in a programming scope. Note that balb::ReservationGuard does not assume ownership of the rate controlling object.

Usage

This section illustrates the intended use of this component.

Example 1: Guarding units reservation in operations with balb::LeakyBucket

Suppose that we are limiting the rate of network traffic generation using a balb::LeakyBucket object. We send data buffer over a network interface using the mySendData function:

bsls::Types::Uint64 mySendData(size_t dataSize);
// Send a specified 'dataSize' amount of data over the network. Return
// the amount of data actually sent. Throw an exception if a network
// failure is detected.

Notice that the mySendData function may throw an exception; therefore, we should wait until mySendData returns before indicating the amount of data sent to the leaky bucket.

Further suppose that multiple threads are sending network data and sharing the same leaky bucket. If every thread simply checks for overflowing of the leaky bucket, send data, and then submit to the leaky bucket, then the rate of data usage may exceed the limits imposed by the leaky bucket due to race conditions. We can avoid the this issue by reserving the amount of data immediately after checking whether the leaky bucket has overflown and submit the reserved amount after the data has been sent. However, this process could lead to the loss of the reserved units (effectively decreasing the leaky bucket's capacity) if mySendData throws an exception. balb::ReservationGuard is designed to resolve this issue.

First, we define the size of each data chunk and the total size of the data to send:

const bsls::Types::Uint64 CHUNK_SIZE = 256;
bsls::Types::Uint64 bytesSent = 0;
bsls::Types::Uint64 totalSize = 10 * 1024; // in bytes

Then, we create a balb::LeakyBucket object to limit the rate of data transmission:

bsls::Types::Uint64 capacity = 1536;
balb::LeakyBucket bucket(rate, capacity, now);
Definition balb_leakybucket.h:421
Definition bsls_timeinterval.h:301
static bsls::TimeInterval now()
Definition bdlt_currenttime.h:290

Next, 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:

while (bytesSent < totalSize) {
if (!bucket.wouldOverflow(now)) {

Now, if the leaky bucket would not overflow, we create a balb::ReservationGuard object to reserve the amount of data to be sent:

CHUNK_SIZE);
Definition balb_reservationguard.h:183

Then, we use the mySendData function to send the data chunk over the network. After the data had been sent, we submit the amount of reserved data that was actually sent:

result = mySendData(CHUNK_SIZE);
bytesSent += result;
guard.submitReserved(result);

Note that we do not have manually cancel any remaining units reserved by the balb::ReservationGuard object either because mySendData threw an exception, or the data was only partially sent, because when the guard object goes out of scope, all remaining reserved units will be automatically cancelled.

}

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);
bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() +
(timeToSubmit.nanoseconds() % 1000) ? 1 : 0;
bslmt::ThreadUtil::microSleep(static_cast<int>(uS));
}
}
BSLS_KEYWORD_CONSTEXPR int nanoseconds() const
Definition bsls_timeinterval.h:1348
BSLS_KEYWORD_CONSTEXPR_CPP14 bsls::Types::Int64 totalMicroseconds() const
Definition bsls_timeinterval.h:1397
static void microSleep(int microseconds, int seconds=0)
Definition bslmt_threadutil.h:955