diff options
Diffstat (limited to 'indra/llcommon/throttle.h')
-rw-r--r-- | indra/llcommon/throttle.h | 138 |
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..481d1d8f72 --- /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) */ |