Skip to content

Commit edea8e8

Browse files
committed
feat: Add ThrottledInvoker utility
Common use case is throttling logging. SDB-9889
1 parent ff08d4f commit edea8e8

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

toolbox/util/Utility.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,37 @@ inline constexpr std::string_view bool_to_alpha(bool b) noexcept
167167
return b ? "true" : "false";
168168
}
169169

170+
/// Throttles the invocation of a callable to not exceed a specified rate.
171+
/// The rate is controlled by a cooldown interval.
172+
///
173+
/// \tparam ClockT The clock type to use for time points.
174+
/// \tparam DurationT The duration type for the cooldown interval.
175+
template <typename ClockT, typename DurationT>
176+
class ThrottledInvoker {
177+
using TimePointT = typename ClockT::time_point;
178+
179+
public:
180+
explicit ThrottledInvoker(DurationT cooldown_interval)
181+
: cooldown_interval_(cooldown_interval) {}
182+
183+
template <typename Callable>
184+
void operator()(TimePointT now, Callable&& callable)
185+
{
186+
if (now < last_time_invoked_) [[unlikely]] {
187+
return;
188+
}
189+
190+
if (duration_cast<DurationT>(now - last_time_invoked_) > cooldown_interval_) {
191+
last_time_invoked_ = now;
192+
std::forward<Callable>(callable)();
193+
}
194+
}
195+
196+
private:
197+
const DurationT cooldown_interval_{};
198+
TimePointT last_time_invoked_{};
199+
};
200+
170201
} // namespace util
171202
} // namespace toolbox
172203

toolbox/util/Utility.ut.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
// limitations under the License.
1616

1717
#include "Utility.hpp"
18+
19+
#include <toolbox/sys/Time.hpp>
20+
1821
#include <cmath>
1922
#include <limits>
23+
#include <thread>
2024

2125
#include <boost/test/unit_test.hpp>
2226

@@ -391,4 +395,31 @@ BOOST_AUTO_TEST_CASE(DecDigitSignedCase)
391395
BOOST_CHECK_EQUAL(dec_digits(std::int64_t{9223372036854775807}), 19);
392396
}
393397

398+
BOOST_AUTO_TEST_CASE(ThrottledInvokerCheck)
399+
{
400+
constexpr auto threshold = 1'000us;
401+
ThrottledInvoker<MonoClock, Micros> throttler{threshold};
402+
403+
std::size_t count = 0;
404+
405+
auto check_throttling = [&] {
406+
auto now = MonoClock::now();
407+
const auto end = now + threshold;
408+
while (now < end) {
409+
throttler(now, [&]() { ++count; });
410+
now = MonoClock::now();
411+
}
412+
};
413+
414+
check_throttling();
415+
BOOST_CHECK_EQUAL(count, 1);
416+
417+
// sleep for a bit to pass the cooldown period
418+
std::this_thread::sleep_for(threshold);
419+
420+
// the throttle should invoke again now
421+
check_throttling();
422+
BOOST_CHECK_EQUAL(count, 2);
423+
}
424+
394425
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)