From adac6085b89d955d684abc1424c7a08e9ed5fbf4 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 25 Nov 2024 15:31:38 -0800 Subject: [PATCH] Defuse throttled_func when it's accidentally engaged (#18235) This change prevents `throttled_func` from reading uninitialized memory under some yet-unkown circumstances. The tl;dr is: This simply moves the callback invocation into the storage. That way we can centrally avoid invoking the callback accidentally. --- src/cascadia/WinRTUtils/inc/ThrottledFunc.h | 2 +- src/inc/til/throttled_func.h | 45 ++++++++++++--------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/cascadia/WinRTUtils/inc/ThrottledFunc.h b/src/cascadia/WinRTUtils/inc/ThrottledFunc.h index 40482faf192..94c42b3362a 100644 --- a/src/cascadia/WinRTUtils/inc/ThrottledFunc.h +++ b/src/cascadia/WinRTUtils/inc/ThrottledFunc.h @@ -113,7 +113,7 @@ class ThrottledFunc : public std::enable_shared_from_this_func, self->_storage.take()); + self->_storage.apply(self->_func); } CATCH_LOG(); } diff --git a/src/inc/til/throttled_func.h b/src/inc/til/throttled_func.h index d250f475af5..7a2d544add3 100644 --- a/src/inc/til/throttled_func.h +++ b/src/inc/til/throttled_func.h @@ -30,12 +30,22 @@ namespace til } } - std::tuple take() + void apply(const auto& func) { - std::unique_lock guard{ _lock }; - auto pendingRunArgs = std::move(*_pendingRunArgs); - _pendingRunArgs.reset(); - return pendingRunArgs; + decltype(_pendingRunArgs) args; + { + std::unique_lock guard{ _lock }; + args = std::exchange(_pendingRunArgs, std::nullopt); + } + // Theoretically it should always have a value, because the throttled_func + // should not call the callback without there being a reason. + // But in practice a failure here was observed at least once. + // It's unknown to me what caused it, so the best we can do is avoid a crash. + assert(args.has_value()); + if (args) + { + std::apply(func, *args); + } } explicit operator bool() const @@ -60,10 +70,12 @@ namespace til return _isPending.exchange(true, std::memory_order_relaxed); } - std::tuple<> take() + void apply(const auto& func) { - reset(); - return {}; + if (_isPending.exchange(false, std::memory_order_relaxed)) + { + func(); + } } void reset() @@ -171,31 +183,24 @@ namespace til void flush() { WaitForThreadpoolTimerCallbacks(_timer.get(), true); - if (_storage) - { - _trailing_edge(); - } + _timer_callback(nullptr, this, nullptr); } private: static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept try { - static_cast(context)->_trailing_edge(); - } - CATCH_LOG() - - void _trailing_edge() - { + const auto self = static_cast(context); if constexpr (Leading) { - _storage.reset(); + self->_storage.reset(); } else { - std::apply(_func, _storage.take()); + self->_storage.apply(self->_func); } } + CATCH_LOG() wil::unique_threadpool_timer _createTimer() {