summaryrefslogtreecommitdiff
path: root/indra/llcommon/throttle.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/throttle.h')
-rw-r--r--indra/llcommon/throttle.h138
1 files changed, 138 insertions, 0 deletions
diff --git a/indra/llcommon/throttle.h b/indra/llcommon/throttle.h
new file mode 100644
index 0000000000..7f5389f464
--- /dev/null
+++ b/indra/llcommon/throttle.h
@@ -0,0 +1,138 @@
+/**
+ * @file throttle.h
+ * @author Nat Goodspeed
+ * @date 2024-08-12
+ * @brief Throttle class
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Copyright (c) 2024, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_THROTTLE_H)
+#define LL_THROTTLE_H
+
+#include "apply.h" // LL::bind_front()
+#include "llerror.h"
+#include <functional>
+#include <iomanip> // std::quoted()
+
+class ThrottleBase
+{
+public:
+ ThrottleBase(F64 interval):
+ mInterval(interval)
+ {}
+
+protected:
+ bool too_fast(); // not const: we update mNext
+ F64 mInterval, mNext{ 0. };
+};
+
+/**
+ * An instance of Throttle mediates calls to some other specified function,
+ * ensuring that it's called no more often than the specified time interval.
+ * Throttle is an abstract base class that delegates the behavior when the
+ * specified interval is exceeded.
+ */
+template <typename SIGNATURE>
+class Throttle: public ThrottleBase
+{
+public:
+ Throttle(const std::string& desc,
+ const std::function<SIGNATURE>& func,
+ F64 interval):
+ ThrottleBase(interval),
+ mDesc(desc),
+ mFunc(func)
+ {}
+ // Constructing Throttle with a member function pointer but without an
+ // instance pointer requires you to pass the instance pointer/reference as
+ // the first argument to operator()().
+ template <typename R, class C>
+ Throttle(const std::string& desc, R C::* method, F64 interval):
+ Throttle(desc, std::mem_fn(method), interval)
+ {}
+ template <typename R, class C>
+ Throttle(const std::string& desc, R C::* method, C* instance, F64 interval):
+ Throttle(desc, LL::bind_front(method, instance), interval)
+ {}
+ template <typename R, class C>
+ Throttle(const std::string& desc, R C::* method, const C* instance, F64 interval):
+ Throttle(desc, LL::bind_front(method, instance), interval)
+ {}
+ virtual ~Throttle() {}
+
+ template <typename... ARGS>
+ auto operator()(ARGS... args)
+ {
+ if (too_fast())
+ {
+ suppress();
+ using rtype = decltype(mFunc(std::forward<ARGS>(args)...));
+ if constexpr (! std::is_same_v<rtype, void>)
+ {
+ return rtype{};
+ }
+ }
+ else
+ {
+ return mFunc(std::forward<ARGS>(args)...);
+ }
+ }
+
+protected:
+ // override with desired behavior when calls come too often
+ virtual void suppress() = 0;
+ const std::string mDesc;
+
+private:
+ std::function<SIGNATURE> mFunc;
+};
+
+/**
+ * An instance of LogThrottle mediates calls to some other specified function,
+ * ensuring that it's called no more often than the specified time interval.
+ * When that interval is exceeded, it logs a message at the specified log
+ * level. It uses LL_MUMBLES_ONCE() logic to prevent spamming, since a too-
+ * frequent call may well be spammy.
+ */
+template <LLError::ELevel LOGLEVEL, typename SIGNATURE>
+class LogThrottle: public Throttle<SIGNATURE>
+{
+ using super = Throttle<SIGNATURE>;
+public:
+ LogThrottle(const std::string& desc,
+ const std::function<SIGNATURE>& func,
+ F64 interval):
+ super(desc, func, interval)
+ {}
+ template <typename R, class C>
+ LogThrottle(const std::string& desc, R C::* method, F64 interval):
+ super(desc, method, interval)
+ {}
+ template <typename R, class C>
+ LogThrottle(const std::string& desc, R C::* method, C* instance, F64 interval):
+ super(desc, method, instance, interval)
+ {}
+ template <typename R, class C>
+ LogThrottle(const std::string& desc, R C::* method, const C* instance, F64 interval):
+ super(desc, method, instance, interval)
+ {}
+
+private:
+ void suppress() override
+ {
+ // Using lllog(), the macro underlying LL_WARNS() et al., allows
+ // specifying compile-time LOGLEVEL. It does NOT support a variable
+ // LOGLEVEL, which is why LOGLEVEL is a non-type template parameter.
+ // See llvlog() for variable support, which is a bit more expensive.
+ // true = only print the log message once
+ lllog(LOGLEVEL, true, "LogThrottle") << std::quoted(super::mDesc)
+ << " called more than once per "
+ << super::mInterval
+ << LL_ENDL;
+ }
+};
+
+#endif /* ! defined(LL_THROTTLE_H) */