2021-06-22 22:16:31 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "til/throttled_func.h"
|
|
|
|
|
|
|
|
// ThrottledFunc is a copy of til::throttled_func,
|
|
|
|
// specialized for the use with a WinRT Dispatcher.
|
|
|
|
template<bool leading, typename... Args>
|
|
|
|
class ThrottledFunc : public std::enable_shared_from_this<ThrottledFunc<leading, Args...>>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using filetime_duration = std::chrono::duration<int64_t, std::ratio<1, 10000000>>;
|
|
|
|
using function = std::function<void(Args...)>;
|
|
|
|
|
|
|
|
// Throttles invocations to the given `func` to not occur more often than `delay`.
|
|
|
|
//
|
|
|
|
// If this is a:
|
|
|
|
// * ThrottledFuncLeading: `func` will be invoked immediately and
|
|
|
|
// further invocations prevented until `delay` time has passed.
|
|
|
|
// * ThrottledFuncTrailing: On the first invocation a timer of `delay` time will
|
|
|
|
// be started. After the timer has expired `func` will be invoked just once.
|
|
|
|
//
|
|
|
|
// After `func` was invoked the state is reset and this cycle is repeated again.
|
|
|
|
ThrottledFunc(
|
Allow `ThrottledFunc` to work on different types of dispatcher (#10187)
#### ⚠️ targets #10051
## Summary of the Pull Request
This updates our `ThrottledFunc`s to take a dispatcher parameter. This means that we can use the `Windows::UI::Core::CoreDispatcher` in the `TermControl`, where there's always a `CoreDispatcher`, and use a `Windows::System::DispatcherQueue` in `ControlCore`/`ControlInteractivity`. When running in-proc, these are always the _same thing_. However, out-of-proc, the core needs a dispatcher queue that's not tied to a UI thread (because the content proces _doesn't have a UI thread!_).
This lets us get rid of the output event, because we don't need to bubble that event out to the `TermControl` to let it throttle that update anymore.
## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5
## PR Checklist
* [x] This is a part of #1256
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Fortunately, `winrt::resume_foreground` works the same on both a `CoreDispatcher` and a `DispatcherQueue`, so this wasn't too hard!
## Validation Steps Performed
This was validated in `dev/migrie/oop/the-whole-thing` (or `dev/migrie/oop/connection-factory`, I forget which), and I made sure that it worked both in-proc and x-proc. Not only that, _it wasn't any slower_!This reverts commit 04b751faa70680bf0296063deacec4657c6ff9d6.
2021-08-09 17:21:59 +02:00
|
|
|
winrt::Windows::System::DispatcherQueue dispatcher,
|
2021-06-22 22:16:31 +02:00
|
|
|
filetime_duration delay,
|
|
|
|
function func) :
|
|
|
|
_dispatcher{ std::move(dispatcher) },
|
|
|
|
_func{ std::move(func) },
|
|
|
|
_timer{ _create_timer() }
|
|
|
|
{
|
|
|
|
const auto d = -delay.count();
|
|
|
|
if (d >= 0)
|
|
|
|
{
|
|
|
|
throw std::invalid_argument("non-positive delay specified");
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&_delay, &d, sizeof(d));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ThrottledFunc uses its `this` pointer when creating _timer.
|
|
|
|
// Since the timer cannot be recreated, instances cannot be moved either.
|
|
|
|
ThrottledFunc(const ThrottledFunc&) = delete;
|
|
|
|
ThrottledFunc& operator=(const ThrottledFunc&) = delete;
|
|
|
|
ThrottledFunc(ThrottledFunc&&) = delete;
|
|
|
|
ThrottledFunc& operator=(ThrottledFunc&&) = 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 Run(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 ModifyPending(F func)
|
|
|
|
{
|
|
|
|
_storage.modify_pending(func);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept
|
|
|
|
{
|
|
|
|
static_cast<ThrottledFunc*>(context)->_trailing_edge();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _leading_edge()
|
|
|
|
{
|
|
|
|
if constexpr (leading)
|
|
|
|
{
|
Allow `ThrottledFunc` to work on different types of dispatcher (#10187)
#### ⚠️ targets #10051
## Summary of the Pull Request
This updates our `ThrottledFunc`s to take a dispatcher parameter. This means that we can use the `Windows::UI::Core::CoreDispatcher` in the `TermControl`, where there's always a `CoreDispatcher`, and use a `Windows::System::DispatcherQueue` in `ControlCore`/`ControlInteractivity`. When running in-proc, these are always the _same thing_. However, out-of-proc, the core needs a dispatcher queue that's not tied to a UI thread (because the content proces _doesn't have a UI thread!_).
This lets us get rid of the output event, because we don't need to bubble that event out to the `TermControl` to let it throttle that update anymore.
## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5
## PR Checklist
* [x] This is a part of #1256
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Fortunately, `winrt::resume_foreground` works the same on both a `CoreDispatcher` and a `DispatcherQueue`, so this wasn't too hard!
## Validation Steps Performed
This was validated in `dev/migrie/oop/the-whole-thing` (or `dev/migrie/oop/connection-factory`, I forget which), and I made sure that it worked both in-proc and x-proc. Not only that, _it wasn't any slower_!This reverts commit 04b751faa70680bf0296063deacec4657c6ff9d6.
2021-08-09 17:21:59 +02:00
|
|
|
_dispatcher.TryEnqueue(winrt::Windows::System::DispatcherQueuePriority::Normal, [weakSelf = this->weak_from_this()]() {
|
2021-06-22 22:16:31 +02:00
|
|
|
if (auto self{ weakSelf.lock() })
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
self->_func();
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
|
2021-09-14 22:09:12 +02:00
|
|
|
SetThreadpoolTimer(self->_timer.get(), &self->_delay, 0, 0);
|
2021-06-22 22:16:31 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-14 22:09:12 +02:00
|
|
|
SetThreadpoolTimer(_timer.get(), &_delay, 0, 0);
|
2021-06-22 22:16:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _trailing_edge()
|
|
|
|
{
|
|
|
|
if constexpr (leading)
|
|
|
|
{
|
|
|
|
_storage.reset();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Allow `ThrottledFunc` to work on different types of dispatcher (#10187)
#### ⚠️ targets #10051
## Summary of the Pull Request
This updates our `ThrottledFunc`s to take a dispatcher parameter. This means that we can use the `Windows::UI::Core::CoreDispatcher` in the `TermControl`, where there's always a `CoreDispatcher`, and use a `Windows::System::DispatcherQueue` in `ControlCore`/`ControlInteractivity`. When running in-proc, these are always the _same thing_. However, out-of-proc, the core needs a dispatcher queue that's not tied to a UI thread (because the content proces _doesn't have a UI thread!_).
This lets us get rid of the output event, because we don't need to bubble that event out to the `TermControl` to let it throttle that update anymore.
## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5
## PR Checklist
* [x] This is a part of #1256
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Fortunately, `winrt::resume_foreground` works the same on both a `CoreDispatcher` and a `DispatcherQueue`, so this wasn't too hard!
## Validation Steps Performed
This was validated in `dev/migrie/oop/the-whole-thing` (or `dev/migrie/oop/connection-factory`, I forget which), and I made sure that it worked both in-proc and x-proc. Not only that, _it wasn't any slower_!This reverts commit 04b751faa70680bf0296063deacec4657c6ff9d6.
2021-08-09 17:21:59 +02:00
|
|
|
_dispatcher.TryEnqueue(winrt::Windows::System::DispatcherQueuePriority::Normal, [weakSelf = this->weak_from_this()]() {
|
2021-06-22 22:16:31 +02:00
|
|
|
if (auto self{ weakSelf.lock() })
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::apply(self->_func, self->_storage.take());
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline wil::unique_threadpool_timer _create_timer()
|
|
|
|
{
|
|
|
|
wil::unique_threadpool_timer timer{ CreateThreadpoolTimer(&_timer_callback, this, nullptr) };
|
|
|
|
THROW_LAST_ERROR_IF(!timer);
|
|
|
|
return timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILETIME _delay;
|
Allow `ThrottledFunc` to work on different types of dispatcher (#10187)
#### ⚠️ targets #10051
## Summary of the Pull Request
This updates our `ThrottledFunc`s to take a dispatcher parameter. This means that we can use the `Windows::UI::Core::CoreDispatcher` in the `TermControl`, where there's always a `CoreDispatcher`, and use a `Windows::System::DispatcherQueue` in `ControlCore`/`ControlInteractivity`. When running in-proc, these are always the _same thing_. However, out-of-proc, the core needs a dispatcher queue that's not tied to a UI thread (because the content proces _doesn't have a UI thread!_).
This lets us get rid of the output event, because we don't need to bubble that event out to the `TermControl` to let it throttle that update anymore.
## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5
## PR Checklist
* [x] This is a part of #1256
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Fortunately, `winrt::resume_foreground` works the same on both a `CoreDispatcher` and a `DispatcherQueue`, so this wasn't too hard!
## Validation Steps Performed
This was validated in `dev/migrie/oop/the-whole-thing` (or `dev/migrie/oop/connection-factory`, I forget which), and I made sure that it worked both in-proc and x-proc. Not only that, _it wasn't any slower_!This reverts commit 04b751faa70680bf0296063deacec4657c6ff9d6.
2021-08-09 17:21:59 +02:00
|
|
|
winrt::Windows::System::DispatcherQueue _dispatcher;
|
2021-06-22 22:16:31 +02:00
|
|
|
function _func;
|
|
|
|
|
|
|
|
wil::unique_threadpool_timer _timer;
|
|
|
|
til::details::throttled_func_storage<Args...> _storage;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
using ThrottledFuncTrailing = ThrottledFunc<false, Args...>;
|
|
|
|
using ThrottledFuncLeading = ThrottledFunc<true>;
|