Repository: microsoft/terminal
File: src/cascadia/TerminalControl/ThrottledFunc.h (C/C++)

Code before:

                        std::optional<std::tuple<Args...>> args;
                        {
                            std::lock_guard guard{ self->_lock };
                            self->_pendingRunArgs.swap(args);
                        }
                        std::apply(self->_func, args.value());
                    }
                });
                timer.Start();
            }
        });
    }

    // Method Description:
    // - Modifies the pending arguments for the next function invocation, if
    //   there is one pending currently.
    // - Let's say that you just called the `Run` method with some arguments.
    //   After the delay specified in the constructor, the function specified
    //   in the constructor will be called with these arguments.
    // - By using this method, you can modify the arguments before the function
    //   is called.
    // - You pass a function to this method which will take references to
    //   the arguments (one argument corresponds to one reference to an
    //   argument) and will modify them.
    // - When there is no pending invocation of the function, this method will
    //   not do anything.
    // - This method is always thread-safe. It can be called multiple times on
    //   different threads.
    // Arguments:
    // - f: the function to call with references to the arguments
    // Return Value:
    // - <none>
    template<typename F>
    void ModifyPending(F f)
    {
        std::lock_guard guard{ _lock };

        if (_pendingRunArgs.has_value())
        {
            std::apply(f, _pendingRunArgs.value());
        }
    }

private:
    Func _func;
    winrt::Windows::Foundation::TimeSpan _delay;
    winrt::Windows::UI::Core::CoreDispatcher _dispatcher;

    std::optional<std::tuple<Args...>> _pendingRunArgs;
    std::mutex _lock;

Review comment (suggestion):
FYI: I've tried using a lambda instead of a static function, but the code turned out to be about as ugly (due to the need to explicitly capture every member variable). I've chosen the static function as it at least ensures that the `this` pointer isn't accidentally used.

Code after:
    // - By using this method, you can modify the arguments before the function
    //   is called.
    // - You pass a function to this method which will take references to
    //   the arguments (one argument corresponds to one reference to an
    //   argument) and will modify them.
    // - When there is no pending invocation of the function, this method will
    //   not do anything.
    // - This method is always thread-safe. It can be called multiple times on
    //   different threads.
    // Arguments:
    // - f: the function to call with references to the arguments
    // Return Value:
    // - <none>
    template<typename F>
    void ModifyPending(F f)
    {
        std::lock_guard guard{ _lock };

        if (_pendingRunArgs.has_value())
        {
            std::apply(f, _pendingRunArgs.value());
        }
    }

private:
    static winrt::fire_and_forget _Fire(winrt::Windows::Foundation::TimeSpan delay, winrt::Windows::UI::Core::CoreDispatcher dispatcher, std::weak_ptr<ThrottledFunc> weakThis)
    {
        co_await winrt::resume_after(delay);
        co_await winrt::resume_foreground(dispatcher);

        if (auto self{ weakThis.lock() })
        {
            std::optional<std::tuple<Args...>> args;
            {
                std::lock_guard guard{ self->_lock };
                self->_pendingRunArgs.swap(args);
            }

            std::apply(self->_func, args.value());
        }
    }

    Func _func;
    winrt::Windows::Foundation::TimeSpan _delay;
    winrt::Windows::UI::Core::CoreDispatcher _dispatcher;

    std::mutex _lock;
    std::optional<std::tuple<Args...>> _pendingRunArgs;
};

// Class Description: