Coverage Report

Created: 2026-05-21 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/include/tscore/Throttler.h
Line
Count
Source
1
/** @file
2
3
  A class for generic throttling.
4
5
  @section license License
6
7
  Licensed to the Apache Software Foundation (ASF) under one
8
  or more contributor license agreements.  See the NOTICE file
9
  distributed with this work for additional information
10
  regarding copyright ownership.  The ASF licenses this file
11
  to you under the Apache License, Version 2.0 (the
12
  "License"); you may not use this file except in compliance
13
  with the License.  You may obtain a copy of the License at
14
15
      http://www.apache.org/licenses/LICENSE-2.0
16
17
  Unless required by applicable law or agreed to in writing, software
18
  distributed under the License is distributed on an "AS IS" BASIS,
19
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
  See the License for the specific language governing permissions and
21
  limitations under the License.
22
 */
23
24
#pragma once
25
26
#include <atomic>
27
#include <chrono>
28
#include <cstdint>
29
30
/** A class that exposes an interface for generic throttling of some action
31
 * against a certain interval.
32
 *
33
 * To use:
34
 *
35
 * 1. Create an instance of this class specifying the interval for which
36
 * something should be throttled. Alternatively, inherit from this class to
37
 * have the throttling interface apply to the object you want throttling for.
38
 *
39
 * 2. Prepend each decision for a given throttled action with a call to
40
 * is_throttled.
41
 *
42
 *   2a. If the is_throttled is false, then at least the configured number of
43
 *   microseconds has elapsed since the previous call in which is_throttled
44
 *   returned false. The number of times the check has been called between
45
 *   these two times is provided in the suppressed_count output parameter.
46
 *
47
 *   2b. If is_throttled returns true, then not enough time has elapsed
48
 *   since the last time the operation returned true per the throttling
49
 *   interval. Thus the operation should be skipped or suppressed, depending
50
 *   upon the context.
51
 *
52
 * For instance:
53
 *
54
 *    void foo()
55
 *    {
56
 *      using namespace std::chrono_literals;
57
 *      static Throttler t(300ms);
58
 *      uint64_t suppressed_count;
59
 *      if (!t.is_throttled(suppressed_count)) {
60
 *        std::printf("Alan bought another monitor\n");
61
 *        std::printf("We ignored Alan buying a monitor %llu times\n", suppressed_count);
62
 *      }
63
 *    }
64
 */
65
class Throttler
66
{
67
public:
68
0
  virtual ~Throttler() = default;
69
70
  /**
71
   * @param[in] interval The minimum number of microseconds between
72
   * calls to Throttler which should return true.
73
   */
74
  Throttler(std::chrono::microseconds interval);
75
76
  /** Whether the current event should be suppressed because the time since the
77
   * last unsuppressed event is less than the throttling interval.
78
   *
79
   * @param[out] suppressed_count If the return of this call is false (the action
80
   * should not be suppressed), this is populated with the approximate number
81
   * of suppressed events between the last unsuppressed event and the current
82
   * one.  Otherwise the value is not set. This value is approximate because,
83
   * if used in a multithreaded context, other threads may be querrying against
84
   * this function as well concurrently, and their count may not be applied
85
   * depending upon the timing of their query.
86
   *
87
   * @return True if the action is suppressed per the configured interval,
88
   * false otherwise.
89
   */
90
  bool is_throttled(uint64_t &suppressed_count);
91
92
  /** Set the log throttling interval to a new value.
93
   *
94
   * @param[in] interval The new interval to set.
95
   */
96
  void set_throttling_interval(std::chrono::microseconds new_interval);
97
98
  /** Manually reset the throttling counter to the current time.
99
   *
100
   * @return the number of messages skipped since the previous positive return
101
   * of the functor operator.
102
   */
103
  uint64_t reset_counter();
104
105
private:
106
  /// Base clock.
107
  using Clock = std::chrono::steady_clock;
108
109
  /** A time_point with a noexcept constructor.
110
   *
111
   * This is a workaround for older gcc and clang compilers which implemented an
112
   * older version of the standard which made atomic's noexcept construction
113
   * specification not compatible with time_point's undecorated constructor.
114
   */
115
  class TimePoint : public Clock::time_point
116
  {
117
  public:
118
    using time_point::time_point;
119
120
    // This noexcept specification makes TimePoint compatible with older
121
    // compiler implementations of atomic.
122
5
    constexpr TimePoint() noexcept : time_point() {}
123
124
0
    template <class Duration2> constexpr TimePoint(const time_point<Clock, Duration2> &t) : time_point(t) {}
125
  };
126
127
  /// Time that the last item was emitted.
128
  std::atomic<TimePoint> _last_allowed_time{TimePoint()};
129
130
  /// The minimum number of microseconds desired between actions.
131
  std::atomic<std::chrono::microseconds> _interval{std::chrono::microseconds{0}};
132
133
  /// The number of calls to Throttler since the last
134
  uint64_t _suppressed_count = 0;
135
};