Browse Source

chore: stop leaking v8 environment (#22761)

Jeremy Apthorp 5 years ago
parent
commit
07a049ef1b

+ 8 - 4
shell/browser/api/electron_api_event_emitter.cc

@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/no_destructor.h"
 #include "gin/dictionary.h"
 #include "shell/common/gin_converters/callback_converter.h"
 #include "shell/common/node_includes.h"
@@ -13,11 +14,14 @@
 
 namespace {
 
-v8::Global<v8::Object> event_emitter_prototype;
+v8::Global<v8::Object>* GetEventEmitterPrototypeReference() {
+  static base::NoDestructor<v8::Global<v8::Object>> event_emitter_prototype;
+  return event_emitter_prototype.get();
+}
 
 void SetEventEmitterPrototype(v8::Isolate* isolate,
                               v8::Local<v8::Object> proto) {
-  event_emitter_prototype.Reset(isolate, proto);
+  GetEventEmitterPrototypeReference()->Reset(isolate, proto);
 }
 
 void Initialize(v8::Local<v8::Object> exports,
@@ -36,8 +40,8 @@ void Initialize(v8::Local<v8::Object> exports,
 namespace electron {
 
 v8::Local<v8::Object> GetEventEmitterPrototype(v8::Isolate* isolate) {
-  CHECK(!event_emitter_prototype.IsEmpty());
-  return event_emitter_prototype.Get(isolate);
+  CHECK(!GetEventEmitterPrototypeReference()->IsEmpty());
+  return GetEventEmitterPrototypeReference()->Get(isolate);
 }
 
 }  // namespace electron

+ 0 - 9
shell/browser/electron_browser_main_parts.cc

@@ -227,15 +227,6 @@ ElectronBrowserMainParts::ElectronBrowserMainParts(
 
 ElectronBrowserMainParts::~ElectronBrowserMainParts() {
   asar::ClearArchives();
-  // Leak the JavascriptEnvironment on exit.
-  // This is to work around the bug that V8 would be waiting for background
-  // tasks to finish on exit, while somehow it waits forever in Electron, more
-  // about this can be found at
-  // https://github.com/electron/electron/issues/4767. On the other handle there
-  // is actually no need to gracefully shutdown V8 on exit in the main process,
-  // we already ensured all necessary resources get cleaned up, and it would
-  // make quitting faster.
-  ignore_result(js_env_.release());
 }
 
 // static

+ 1 - 1
shell/browser/electron_browser_main_parts.h

@@ -129,8 +129,8 @@ class ElectronBrowserMainParts : public content::BrowserMainParts {
   // Pointer to exit code.
   int* exit_code_ = nullptr;
 
-  std::unique_ptr<Browser> browser_;
   std::unique_ptr<JavascriptEnvironment> js_env_;
+  std::unique_ptr<Browser> browser_;
   std::unique_ptr<NodeBindings> node_bindings_;
   std::unique_ptr<ElectronBindings> electron_bindings_;
   std::unique_ptr<NodeEnvironment> node_env_;

+ 15 - 8
shell/common/gin_helper/destroyable.cc

@@ -4,6 +4,7 @@
 
 #include "shell/common/gin_helper/destroyable.h"
 
+#include "base/no_destructor.h"
 #include "gin/converter.h"
 #include "shell/common/gin_helper/wrappable_base.h"
 
@@ -11,9 +12,15 @@ namespace gin_helper {
 
 namespace {
 
-// Cached function templates, leaked on exit. (They are leaked in V8 anyway.)
-v8::Global<v8::FunctionTemplate> g_destroy_func;
-v8::Global<v8::FunctionTemplate> g_is_destroyed_func;
+v8::Global<v8::FunctionTemplate>* GetDestroyFunc() {
+  static base::NoDestructor<v8::Global<v8::FunctionTemplate>> destroy_func;
+  return destroy_func.get();
+}
+
+v8::Global<v8::FunctionTemplate>* GetIsDestroyedFunc() {
+  static base::NoDestructor<v8::Global<v8::FunctionTemplate>> is_destroyed_func;
+  return is_destroyed_func.get();
+}
 
 void DestroyFunc(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Local<v8::Object> holder = info.Holder();
@@ -45,22 +52,22 @@ bool Destroyable::IsDestroyed(v8::Local<v8::Object> object) {
 void Destroyable::MakeDestroyable(v8::Isolate* isolate,
                                   v8::Local<v8::FunctionTemplate> prototype) {
   // Cache the FunctionTemplate of "destroy" and "isDestroyed".
-  if (g_destroy_func.IsEmpty()) {
+  if (GetDestroyFunc()->IsEmpty()) {
     auto templ = v8::FunctionTemplate::New(isolate, DestroyFunc);
     templ->RemovePrototype();
-    g_destroy_func.Reset(isolate, templ);
+    GetDestroyFunc()->Reset(isolate, templ);
     templ = v8::FunctionTemplate::New(isolate, IsDestroyedFunc);
     templ->RemovePrototype();
-    g_is_destroyed_func.Reset(isolate, templ);
+    GetIsDestroyedFunc()->Reset(isolate, templ);
   }
 
   auto proto_templ = prototype->PrototypeTemplate();
   proto_templ->Set(
       gin::StringToSymbol(isolate, "destroy"),
-      v8::Local<v8::FunctionTemplate>::New(isolate, g_destroy_func));
+      v8::Local<v8::FunctionTemplate>::New(isolate, *GetDestroyFunc()));
   proto_templ->Set(
       gin::StringToSymbol(isolate, "isDestroyed"),
-      v8::Local<v8::FunctionTemplate>::New(isolate, g_is_destroyed_func));
+      v8::Local<v8::FunctionTemplate>::New(isolate, *GetIsDestroyedFunc()));
 }
 
 }  // namespace gin_helper