From cc5aa65cb421ffa0df823cae09a3dacb0485e698 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 6 Sep 2024 07:16:56 -0500 Subject: [PATCH] fix: delete UvTaskRunner's timers only after they're closed (#43561) * fix: free UvTaskRunner timers only after they are closed * refactor: UvTaskRunner now holds UvHandles --- shell/app/uv_task_runner.cc | 44 ++++++++++++------------------------ shell/app/uv_task_runner.h | 6 ++--- shell/common/node_bindings.h | 27 +++++++++++++++++++++- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/shell/app/uv_task_runner.cc b/shell/app/uv_task_runner.cc index e8cfc22860..606f384dbc 100644 --- a/shell/app/uv_task_runner.cc +++ b/shell/app/uv_task_runner.cc @@ -2,31 +2,33 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. +#include "shell/app/uv_task_runner.h" + #include #include "base/location.h" #include "base/time/time.h" -#include "shell/app/uv_task_runner.h" namespace electron { -UvTaskRunner::UvTaskRunner(uv_loop_t* loop) : loop_(loop) {} +UvTaskRunner::UvTaskRunner(uv_loop_t* loop) : loop_{loop} {} -UvTaskRunner::~UvTaskRunner() { - for (auto& iter : tasks_) { - uv_unref(reinterpret_cast(iter.first)); - delete iter.first; - } -} +UvTaskRunner::~UvTaskRunner() = default; bool UvTaskRunner::PostDelayedTask(const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay) { - auto* timer = new uv_timer_t; + auto on_timeout = [](uv_timer_t* timer) { + auto& tasks = static_cast(timer->data)->tasks_; + if (auto iter = tasks.find(timer); iter != tasks.end()) + std::move(tasks.extract(iter).mapped()).Run(); + }; + + auto timer = UvHandle{}; timer->data = this; - uv_timer_init(loop_, timer); - uv_timer_start(timer, UvTaskRunner::OnTimeout, delay.InMilliseconds(), 0); - tasks_[timer] = std::move(task); + uv_timer_init(loop_, timer.get()); + uv_timer_start(timer.get(), on_timeout, delay.InMilliseconds(), 0); + tasks_.insert_or_assign(std::move(timer), std::move(task)); return true; } @@ -40,22 +42,4 @@ bool UvTaskRunner::PostNonNestableDelayedTask(const base::Location& from_here, return PostDelayedTask(from_here, std::move(task), delay); } -// static -void UvTaskRunner::OnTimeout(uv_timer_t* timer) { - auto& tasks = static_cast(timer->data)->tasks_; - const auto iter = tasks.find(timer); - if (iter == std::end(tasks)) - return; - - std::move(iter->second).Run(); - tasks.erase(iter); - uv_timer_stop(timer); - uv_close(reinterpret_cast(timer), UvTaskRunner::OnClose); -} - -// static -void UvTaskRunner::OnClose(uv_handle_t* handle) { - delete reinterpret_cast(handle); -} - } // namespace electron diff --git a/shell/app/uv_task_runner.h b/shell/app/uv_task_runner.h index 7f23826db1..2802899063 100644 --- a/shell/app/uv_task_runner.h +++ b/shell/app/uv_task_runner.h @@ -9,7 +9,7 @@ #include "base/memory/raw_ptr.h" #include "base/task/single_thread_task_runner.h" -#include "uv.h" // NOLINT(build/include_directory) +#include "shell/common/node_bindings.h" namespace base { class Location; @@ -38,12 +38,10 @@ class UvTaskRunner : public base::SingleThreadTaskRunner { private: ~UvTaskRunner() override; - static void OnTimeout(uv_timer_t* timer); - static void OnClose(uv_handle_t* handle); raw_ptr loop_; - std::map tasks_; + std::map, base::OnceClosure, UvHandleCompare> tasks_; }; } // namespace electron diff --git a/shell/common/node_bindings.h b/shell/common/node_bindings.h index ad55645422..ac6faf470a 100644 --- a/shell/common/node_bindings.h +++ b/shell/common/node_bindings.h @@ -15,6 +15,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/memory/weak_ptr.h" +#include "base/types/to_address.h" #include "gin/public/context_holder.h" #include "gin/public/gin_embedders.h" #include "uv.h" // NOLINT(build/include_directory) @@ -58,11 +59,25 @@ template ::value>::type* = nullptr> class UvHandle { public: - UvHandle() : t_(new T) {} + UvHandle() : t_{new T} {} ~UvHandle() { reset(); } + + UvHandle(UvHandle&&) = default; + UvHandle& operator=(UvHandle&&) = default; + + UvHandle(const UvHandle&) = delete; + UvHandle& operator=(const UvHandle&) = delete; + T* get() { return t_; } + T* operator->() { return t_; } + const T* get() const { return t_; } + const T* operator->() const { return t_; } + uv_handle_t* handle() { return reinterpret_cast(t_); } + // compare by handle pointer address + auto operator<=>(const UvHandle& that) const = default; + void reset() { auto* h = handle(); if (h != nullptr) { @@ -80,6 +95,16 @@ class UvHandle { RAW_PTR_EXCLUSION T* t_ = {}; }; +// Helper for comparing UvHandles and raw uv pointers, e.g. as map keys +struct UvHandleCompare { + using is_transparent = void; + + template + bool operator()(U const& u, V const& v) const { + return base::to_address(u) < base::to_address(v); + } +}; + class NodeBindings { public: enum class BrowserEnvironment { kBrowser, kRenderer, kUtility, kWorker };