Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add a wrapper for wrangling uv handles. #25662

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 4 additions & 6 deletions shell/common/api/electron_bindings.cc
Expand Up @@ -56,14 +56,12 @@ void V8FatalErrorCallback(const char* location, const char* message) {
} // namespace

ElectronBindings::ElectronBindings(uv_loop_t* loop) {
uv_async_init(loop, &call_next_tick_async_, OnCallNextTick);
call_next_tick_async_.data = this;
uv_async_init(loop, call_next_tick_async_.get(), OnCallNextTick);
call_next_tick_async_.get()->data = this;
metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
}

ElectronBindings::~ElectronBindings() {
uv_close(reinterpret_cast<uv_handle_t*>(&call_next_tick_async_), nullptr);
}
ElectronBindings::~ElectronBindings() {}

// static
void ElectronBindings::BindProcess(v8::Isolate* isolate,
Expand Down Expand Up @@ -131,7 +129,7 @@ void ElectronBindings::ActivateUVLoop(v8::Isolate* isolate) {
return;

pending_next_ticks_.push_back(env);
uv_async_send(&call_next_tick_async_);
uv_async_send(call_next_tick_async_.get());
}

// static
Expand Down
3 changes: 2 additions & 1 deletion shell/common/api/electron_bindings.h
Expand Up @@ -14,6 +14,7 @@
#include "base/process/process_metrics.h"
#include "base/strings/string16.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_bindings.h"
#include "uv.h" // NOLINT(build/include_directory)

namespace gin_helper {
Expand Down Expand Up @@ -74,7 +75,7 @@ class ElectronBindings {
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);

uv_async_t call_next_tick_async_;
UvHandle<uv_async_t> call_next_tick_async_;
std::list<node::Environment*> pending_next_ticks_;
std::unique_ptr<base::ProcessMetrics> metrics_;

Expand Down
43 changes: 22 additions & 21 deletions shell/common/node_bindings.cc
Expand Up @@ -97,24 +97,26 @@ namespace {

void stop_and_close_uv_loop(uv_loop_t* loop) {
uv_stop(loop);
int error = uv_loop_close(loop);

while (error) {
uv_run(loop, UV_RUN_DEFAULT);
uv_stop(loop);
uv_walk(
loop,
[](uv_handle_t* handle, void*) {
if (!uv_is_closing(handle)) {
uv_close(handle, nullptr);
}
},
nullptr);
uv_run(loop, UV_RUN_DEFAULT);
error = uv_loop_close(loop);
}

DCHECK_EQ(error, 0);
auto const ensure_closing = [](uv_handle_t* handle, void*) {
// We should be using the UvHandle wrapper everywhere, in which case
// all handles should already be in a closing state...
DCHECK(uv_is_closing(handle));
// ...but if a raw handle got through, through, do the right thing anyway
if (!uv_is_closing(handle)) {
uv_close(handle, nullptr);
}
};

uv_walk(loop, ensure_closing, nullptr);

// All remaining handles are in a closing state now.
// Pump the event loop so that they can finish closing.
for (;;)
if (uv_run(loop, UV_RUN_DEFAULT) == 0)
break;

DCHECK_EQ(0, uv_loop_alive(loop));
}

bool g_is_initialized = false;
Expand Down Expand Up @@ -282,7 +284,7 @@ NodeBindings::~NodeBindings() {

// Clear uv.
uv_sem_destroy(&embed_sem_);
uv_close(reinterpret_cast<uv_handle_t*>(&dummy_uv_handle_), nullptr);
dummy_uv_handle_.reset();

// Clean up worker loop
if (in_worker_loop())
Expand Down Expand Up @@ -460,7 +462,7 @@ void NodeBindings::LoadEnvironment(node::Environment* env) {
void NodeBindings::PrepareMessageLoop() {
// Add dummy handle for libuv, otherwise libuv would quit when there is
// nothing to do.
uv_async_init(uv_loop_, &dummy_uv_handle_, nullptr);
uv_async_init(uv_loop_, dummy_uv_handle_.get(), nullptr);

// Start worker that will interrupt main loop when having uv events.
uv_sem_init(&embed_sem_, 0);
Expand Down Expand Up @@ -517,8 +519,7 @@ void NodeBindings::WakeupMainThread() {
}

void NodeBindings::WakeupEmbedThread() {
if (!in_worker_loop())
uv_async_send(&dummy_uv_handle_);
uv_async_send(dummy_uv_handle_.get());
}

// static
Expand Down
52 changes: 51 additions & 1 deletion shell/common/node_bindings.h
Expand Up @@ -5,6 +5,8 @@
#ifndef SHELL_COMMON_NODE_BINDINGS_H_
#define SHELL_COMMON_NODE_BINDINGS_H_

#include <type_traits>

#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
Expand All @@ -24,6 +26,54 @@ class IsolateData;

namespace electron {

// A helper class to manage uv_handle_t types, e.g. uv_async_t.
//
// As per the uv docs: "uv_close() MUST be called on each handle before
// memory is released. Moreover, the memory can only be released in
// close_cb or after it has returned." This class encapsulates the work
// needed to follow those requirements.
template <typename T,
typename std::enable_if<
// these are the C-style 'subclasses' of uv_handle_t
std::is_same<T, uv_async_t>::value ||
std::is_same<T, uv_check_t>::value ||
std::is_same<T, uv_fs_event_t>::value ||
std::is_same<T, uv_fs_poll_t>::value ||
std::is_same<T, uv_idle_t>::value ||
std::is_same<T, uv_pipe_t>::value ||
std::is_same<T, uv_poll_t>::value ||
std::is_same<T, uv_prepare_t>::value ||
std::is_same<T, uv_process_t>::value ||
std::is_same<T, uv_signal_t>::value ||
std::is_same<T, uv_stream_t>::value ||
std::is_same<T, uv_tcp_t>::value ||
std::is_same<T, uv_timer_t>::value ||
std::is_same<T, uv_tty_t>::value ||
std::is_same<T, uv_udp_t>::value>::type* = nullptr>
class UvHandle {
public:
UvHandle() : t_(new T) {}
~UvHandle() { reset(); }
T* get() { return t_; }
uv_handle_t* handle() { return reinterpret_cast<uv_handle_t*>(t_); }

void reset() {
auto* h = handle();
if (h != nullptr) {
DCHECK_EQ(0, uv_is_closing(h));
uv_close(h, OnClosed);
t_ = nullptr;
}
}

private:
static void OnClosed(uv_handle_t* handle) {
delete reinterpret_cast<T*>(handle);
}

T* t_ = {};
};

class NodeBindings {
public:
enum class BrowserEnvironment { BROWSER, RENDERER, WORKER };
Expand Down Expand Up @@ -95,7 +145,7 @@ class NodeBindings {
uv_loop_t worker_loop_;

// Dummy handle to make uv's loop not quit.
uv_async_t dummy_uv_handle_;
UvHandle<uv_async_t> dummy_uv_handle_;

// Thread for polling events.
uv_thread_t embed_thread_;
Expand Down