summaryrefslogtreecommitdiff
path: root/indra/llcommon/throttle.h
blob: 7f5389f46408c6e43642c94033f98ad468de7bad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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) */