- // If there are `tokens` avilable at `nowInSeconds`, consume them and
- // return true. Otherwise, return false.
- //
- // This implementation is written in a lock-free manner using a
- // compare-and-exchange loop, with branch prediction optimized to minimize
- // time spent in the 'success' case which performs a write.
- bool consume(double tokens, double nowInSeconds) noexcept {
- const double secondsNeeded = tokens * std::atomic_load_explicit(
- &secondsPerToken_, std::memory_order_relaxed);
-
- const double minTime = nowInSeconds - std::atomic_load_explicit(
- &secondsPerBurst_, std::memory_order_relaxed);
+ /**
+ * Attempts to consume some number of tokens. Tokens are first added to the
+ * bucket based on the time elapsed since the last attempt to consume tokens.
+ * Note: Attempts to consume more tokens than the burst size will always
+ * fail.
+ *
+ * Thread-safe.
+ *
+ * @param toConsume The number of tokens to consume.
+ * @param rate Number of tokens to generate per second.
+ * @param burstSize Maximum burst size. Must be greater than 0.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ * @return True if the rate limit check passed, false otherwise.
+ */
+ bool consume(
+ double toConsume,
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) {
+ assert(rate > 0);
+ assert(burstSize > 0);
+
+ return this->consumeImpl(
+ rate, burstSize, nowInSeconds, [toConsume](double& tokens) {
+ if (tokens < toConsume) {
+ return false;
+ }
+ tokens -= toConsume;
+ return true;
+ });
+ }