Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component balb_reservationguard
[Package balb]

Provide a generic proctor for rate controlling objects. More...

Namespaces

namespace  balb

Detailed Description

Outline
Purpose:
Provide a generic proctor for rate controlling objects.
Classes:
balb::ReservationGuard a guard for reserving resources from rate limiters.
See also:
Component balb_leakybucket, Component 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);
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 rate     = 512;
  bsls::Types::Uint64 capacity = 1536;
  bsls::TimeInterval  now      = bdlt::CurrentTime::now();
  balb::LeakyBucket   bucket(rate, capacity, now);
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) {

      now = bdlt::CurrentTime::now();
      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:
          balb::ReservationGuard<balb::LeakyBucket> guard(&bucket,
                                                          CHUNK_SIZE);
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:
          bsls::Types::Uint64 result;
          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));
      }
  }