fix: raw_ptr destruction order in NodeBindings (#39762)

This commit is contained in:
Charles Kerr 2023-09-07 18:25:17 -05:00 committed by GitHub
parent 0b44f433c8
commit 792037b338
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 44 deletions

View file

@ -394,21 +394,11 @@ base::FilePath GetResourcesPath() {
return exec_path.DirName().Append(FILE_PATH_LITERAL("resources")); return exec_path.DirName().Append(FILE_PATH_LITERAL("resources"));
#endif #endif
} }
} // namespace } // namespace
NodeBindings::NodeBindings(BrowserEnvironment browser_env) NodeBindings::NodeBindings(BrowserEnvironment browser_env)
: browser_env_(browser_env) { : browser_env_{browser_env},
if (browser_env == BrowserEnvironment::kWorker) { uv_loop_{InitEventLoop(browser_env, &worker_loop_)} {}
uv_loop_init(&worker_loop_);
uv_loop_ = &worker_loop_;
} else {
uv_loop_ = uv_default_loop();
}
// Interrupt embed polling when a handle is started.
uv_loop_configure(uv_loop_, UV_LOOP_INTERRUPT_ON_IO_CHANGE);
}
NodeBindings::~NodeBindings() { NodeBindings::~NodeBindings() {
// Quit the embed thread. // Quit the embed thread.
@ -429,6 +419,24 @@ NodeBindings::~NodeBindings() {
stop_and_close_uv_loop(uv_loop_); stop_and_close_uv_loop(uv_loop_);
} }
// static
uv_loop_t* NodeBindings::InitEventLoop(BrowserEnvironment browser_env,
uv_loop_t* worker_loop) {
uv_loop_t* event_loop = nullptr;
if (browser_env == BrowserEnvironment::kWorker) {
uv_loop_init(worker_loop);
event_loop = worker_loop;
} else {
event_loop = uv_default_loop();
}
// Interrupt embed polling when a handle is started.
uv_loop_configure(event_loop, UV_LOOP_INTERRUPT_ON_IO_CHANGE);
return event_loop;
}
void NodeBindings::RegisterBuiltinBindings() { void NodeBindings::RegisterBuiltinBindings() {
#define V(modname) _register_##modname(); #define V(modname) _register_##modname();
if (IsBrowserProcess()) { if (IsBrowserProcess()) {

View file

@ -132,9 +132,7 @@ class NodeBindings {
void set_uv_env(node::Environment* env) { uv_env_ = env; } void set_uv_env(node::Environment* env) { uv_env_ = env; }
node::Environment* uv_env() const { return uv_env_; } node::Environment* uv_env() const { return uv_env_; }
uv_loop_t* uv_loop() const { return uv_loop_; } [[nodiscard]] constexpr uv_loop_t* uv_loop() { return uv_loop_; }
bool in_worker_loop() const { return uv_loop_ == &worker_loop_; }
// disable copy // disable copy
NodeBindings(const NodeBindings&) = delete; NodeBindings(const NodeBindings&) = delete;
@ -150,25 +148,36 @@ class NodeBindings {
// Called to poll events in new thread. // Called to poll events in new thread.
virtual void PollEvents() = 0; virtual void PollEvents() = 0;
// Run the libuv loop for once.
void UvRunOnce();
// Make the main thread run libuv loop. // Make the main thread run libuv loop.
void WakeupMainThread(); void WakeupMainThread();
// Interrupt the PollEvents. // Interrupt the PollEvents.
void WakeupEmbedThread(); void WakeupEmbedThread();
private:
static uv_loop_t* InitEventLoop(BrowserEnvironment browser_env,
uv_loop_t* worker_loop);
// Run the libuv loop for once.
void UvRunOnce();
[[nodiscard]] constexpr bool in_worker_loop() const {
return browser_env_ == BrowserEnvironment::kWorker;
}
// Which environment we are running. // Which environment we are running.
const BrowserEnvironment browser_env_; const BrowserEnvironment browser_env_;
// Loop used when constructed in WORKER mode
uv_loop_t worker_loop_;
// Current thread's libuv loop.
// depends-on: worker_loop_
const raw_ptr<uv_loop_t> uv_loop_;
// Current thread's MessageLoop. // Current thread's MessageLoop.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Current thread's libuv loop.
raw_ptr<uv_loop_t> uv_loop_;
private:
// Choose a reasonable unique index that's higher than any Blink uses // Choose a reasonable unique index that's higher than any Blink uses
// and thus unlikely to collide with an existing index. // and thus unlikely to collide with an existing index.
static constexpr int kElectronContextEmbedderDataIndex = static constexpr int kElectronContextEmbedderDataIndex =
@ -192,9 +201,6 @@ class NodeBindings {
// Whether the libuv loop has ended. // Whether the libuv loop has ended.
bool embed_closed_ = false; bool embed_closed_ = false;
// Loop used when constructed in WORKER mode
uv_loop_t worker_loop_;
// Dummy handle to make uv's loop not quit. // Dummy handle to make uv's loop not quit.
UvHandle<uv_async_t> dummy_uv_handle_; UvHandle<uv_async_t> dummy_uv_handle_;

View file

@ -10,7 +10,9 @@ namespace electron {
NodeBindingsLinux::NodeBindingsLinux(BrowserEnvironment browser_env) NodeBindingsLinux::NodeBindingsLinux(BrowserEnvironment browser_env)
: NodeBindings(browser_env), epoll_(epoll_create(1)) { : NodeBindings(browser_env), epoll_(epoll_create(1)) {
int backend_fd = uv_backend_fd(uv_loop_); auto* const event_loop = uv_loop();
int backend_fd = uv_backend_fd(event_loop);
struct epoll_event ev = {0}; struct epoll_event ev = {0};
ev.events = EPOLLIN; ev.events = EPOLLIN;
ev.data.fd = backend_fd; ev.data.fd = backend_fd;
@ -18,7 +20,9 @@ NodeBindingsLinux::NodeBindingsLinux(BrowserEnvironment browser_env)
} }
void NodeBindingsLinux::PollEvents() { void NodeBindingsLinux::PollEvents() {
int timeout = uv_backend_timeout(uv_loop_); auto* const event_loop = uv_loop();
int timeout = uv_backend_timeout(event_loop);
// Wait for new libuv events. // Wait for new libuv events.
int r; int r;

View file

@ -18,15 +18,17 @@ NodeBindingsMac::NodeBindingsMac(BrowserEnvironment browser_env)
: NodeBindings(browser_env) {} : NodeBindings(browser_env) {}
void NodeBindingsMac::PollEvents() { void NodeBindingsMac::PollEvents() {
auto* const event_loop = uv_loop();
struct timeval tv; struct timeval tv;
int timeout = uv_backend_timeout(uv_loop_); int timeout = uv_backend_timeout(event_loop);
if (timeout != -1) { if (timeout != -1) {
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000; tv.tv_usec = (timeout % 1000) * 1000;
} }
fd_set readset; fd_set readset;
int fd = uv_backend_fd(uv_loop_); int fd = uv_backend_fd(event_loop);
FD_ZERO(&readset); FD_ZERO(&readset);
FD_SET(fd, &readset); FD_SET(fd, &readset);

View file

@ -13,34 +13,39 @@ namespace electron {
NodeBindingsWin::NodeBindingsWin(BrowserEnvironment browser_env) NodeBindingsWin::NodeBindingsWin(BrowserEnvironment browser_env)
: NodeBindings(browser_env) { : NodeBindings(browser_env) {
auto* const event_loop = uv_loop();
// on single-core the io comp port NumberOfConcurrentThreads needs to be 2 // on single-core the io comp port NumberOfConcurrentThreads needs to be 2
// to avoid cpu pegging likely caused by a busy loop in PollEvents // to avoid cpu pegging likely caused by a busy loop in PollEvents
if (base::SysInfo::NumberOfProcessors() == 1) { if (base::SysInfo::NumberOfProcessors() == 1) {
// the expectation is the uv_loop_ has just been initialized // the expectation is the event_loop has just been initialized
// which makes iocp replacement safe // which makes iocp replacement safe
CHECK_EQ(0u, uv_loop_->active_handles); CHECK_EQ(0u, event_loop->active_handles);
CHECK_EQ(0u, uv_loop_->active_reqs.count); CHECK_EQ(0u, event_loop->active_reqs.count);
if (uv_loop_->iocp && uv_loop_->iocp != INVALID_HANDLE_VALUE) if (event_loop->iocp && event_loop->iocp != INVALID_HANDLE_VALUE)
CloseHandle(uv_loop_->iocp); CloseHandle(event_loop->iocp);
uv_loop_->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 2); event_loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 2);
} }
} }
void NodeBindingsWin::PollEvents() { void NodeBindingsWin::PollEvents() {
auto* const event_loop = uv_loop();
// If there are other kinds of events pending, uv_backend_timeout will // If there are other kinds of events pending, uv_backend_timeout will
// instruct us not to wait. // instruct us not to wait.
DWORD bytes, timeout; DWORD bytes, timeout;
ULONG_PTR key; ULONG_PTR key;
OVERLAPPED* overlapped; OVERLAPPED* overlapped;
timeout = uv_backend_timeout(uv_loop_); timeout = uv_backend_timeout(event_loop);
GetQueuedCompletionStatus(uv_loop_->iocp, &bytes, &key, &overlapped, timeout); GetQueuedCompletionStatus(event_loop->iocp, &bytes, &key, &overlapped,
timeout);
// Give the event back so libuv can deal with it. // Give the event back so libuv can deal with it.
if (overlapped != NULL) if (overlapped != NULL)
PostQueuedCompletionStatus(uv_loop_->iocp, bytes, key, overlapped); PostQueuedCompletionStatus(event_loop->iocp, bytes, key, overlapped);
} }
// static // static

View file

@ -27,10 +27,10 @@
namespace electron { namespace electron {
ElectronRendererClient::ElectronRendererClient() ElectronRendererClient::ElectronRendererClient()
: node_bindings_( : node_bindings_{NodeBindings::Create(
NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)), NodeBindings::BrowserEnvironment::kRenderer)},
electron_bindings_( electron_bindings_{
std::make_unique<ElectronBindings>(node_bindings_->uv_loop())) {} std::make_unique<ElectronBindings>(node_bindings_->uv_loop())} {}
ElectronRendererClient::~ElectronRendererClient() = default; ElectronRendererClient::~ElectronRendererClient() = default;

View file

@ -51,8 +51,8 @@ class ElectronRendererClient : public RendererClientBase {
// Whether the node integration has been initialized. // Whether the node integration has been initialized.
bool node_integration_initialized_ = false; bool node_integration_initialized_ = false;
std::unique_ptr<NodeBindings> node_bindings_; const std::unique_ptr<NodeBindings> node_bindings_;
std::unique_ptr<ElectronBindings> electron_bindings_; const std::unique_ptr<ElectronBindings> electron_bindings_;
// The node::Environment::GetCurrent API does not return nullptr when it // The node::Environment::GetCurrent API does not return nullptr when it
// is called for a context without node::Environment, so we have to keep // is called for a context without node::Environment, so we have to keep