Repository: microsoft/terminal
File: src/inc/til/throttled_func.h (C/C++)

Code before:
                throw std::invalid_argument("non-positive delay specified");
            }

            memcpy(&_delay, &d, sizeof(d));
        }

        // throttled_func uses its `this` pointer when creating _timer.
        // Since the timer cannot be recreated, instances cannot be moved either.
        throttled_func(const throttled_func&) = delete;
        throttled_func& operator=(const throttled_func&) = delete;
        throttled_func(throttled_func&&) = delete;
        throttled_func& operator=(throttled_func&&) = delete;

        // Throttles the invocation of the function passed to the constructor.
        // If this is a trailing_throttled_func:
        //   If you call this function again before the underlying
        //   timer has expired, the new arguments will be used.
        template<typename... MakeArgs>
        void operator()(MakeArgs&&... args)
        {
            if (!_storage.emplace(std::forward<MakeArgs>(args)...))
            {
                _leading_edge();
            }
        }

        // Modifies the pending arguments for the next function
        // invocation, if there is one pending currently.
        //
        // `func` will be invoked as func(Args...). Make sure to bind any
        // arguments in `func` by reference if you'd like to modify them.
        template<typename F>
        void modify_pending(F func)
        {
            _storage.modify_pending(func);
        }

        // Makes sure that the currently pending timer is executed
        // as soon as possible and in that case waits for its completion.
        // You can use this function in your destructor to ensure that any
        // pending callback invocation is completed as soon as possible.
        //
        // NOTE: Don't call this function if the operator()
        //       could still be called concurrently.
        void flush()
        {
            WaitForThreadpoolTimerCallbacks(_timer.get(), true);
            if (_storage)
            {
                _trailing_edge();
            }

Review comment (suggestion):
oh oh okay I get it now. For a while I was thinking "a debounced trailing func is just a trailing func", but the difference is subtle:

* A trailing func will call the callback {one timeout} after the _first_ call to the `throttled_func`. If there are more calls to the `throttled_func` in that timeout, they'll just update the value which will be used at the end of the first timeout. 
* A debounced trailing func will call the callback {one timeout} after the _last_ call to the `throttled_func`. If there are more calls to the `throttled_func` in that timeout, they'll further delay the callback. 

Code after:
                throw std::invalid_argument("non-positive delay specified");
            }

            memcpy(&_delay, &d, sizeof(d));
        }

        // throttled_func uses its `this` pointer when creating _timer.
        // Since the timer cannot be recreated, instances cannot be moved either.
        throttled_func(const throttled_func&) = delete;
        throttled_func& operator=(const throttled_func&) = delete;
        throttled_func(throttled_func&&) = delete;
        throttled_func& operator=(throttled_func&&) = delete;

        // Throttles the invocation of the function passed to the constructor.
        //
        // If Debounce is true and you call this function again before the
        // underlying timer has expired, its timeout will be reset.
        //
        // If Leading is true and you call this function again before the
        // underlying timer has expired, the new arguments will be used.
        template<typename... MakeArgs>
        void operator()(MakeArgs&&... args)
        {
            const auto hadValue = _storage.emplace(std::forward<MakeArgs>(args)...);

            if constexpr (Debounce)
            {
                SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0);
            }
            else
            {
                if (!hadValue)
                {
                    SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0);
                }
            }

            if constexpr (Leading)
            {
                if (!hadValue)
                {
                    _func();
                }
            }
        }

        // Modifies the pending arguments for the next function
        // invocation, if there is one pending currently.
        //
        // `func` will be invoked as func(Args...). Make sure to bind any
        // arguments in `func` by reference if you'd like to modify them.