Make sure handles of callbacks are releases on exit

Some callbacks are stored in native resources that not managed by
JavaScript, so when those resources are destroyed the JavaScript context
may already be destroyed, and releasing callbacks then would result in
crash.
This commit is contained in:
Cheng Zhao 2015-11-06 20:23:41 +08:00
parent acf4372cf7
commit d70706f876
2 changed files with 66 additions and 11 deletions

View file

@ -4,6 +4,8 @@
#include "atom/common/native_mate_converters/callback.h"
#include "atom/browser/atom_browser_main_parts.h"
namespace mate {
namespace internal {
@ -54,6 +56,33 @@ v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
} // namespace
SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value)
: v8_function_(new RefCountedPersistent<v8::Function>(isolate, value)),
weak_factory_(this) {
Init();
}
SafeV8Function::SafeV8Function(const SafeV8Function& other)
: v8_function_(other.v8_function_),
weak_factory_(this) {
Init();
}
v8::Local<v8::Function> SafeV8Function::NewHandle() const {
return v8_function_->NewHandle();
}
void SafeV8Function::Init() {
if (Locker::IsBrowserProcess() && atom::AtomBrowserMainParts::Get())
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
base::Bind(&SafeV8Function::FreeHandle, weak_factory_.GetWeakPtr()));
}
void SafeV8Function::FreeHandle() {
Locker locker(v8_function_->isolate());
v8_function_ = nullptr;
}
v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Isolate* isolate, const Translater& translater) {
// The FunctionTemplate is cached.

View file

@ -10,6 +10,7 @@
#include "atom/common/api/locker.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "native_mate/function_template.h"
#include "native_mate/scoped_persistent.h"
#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
@ -18,7 +19,24 @@ namespace mate {
namespace internal {
typedef scoped_refptr<RefCountedPersistent<v8::Function> > SafeV8Function;
// Manages the V8 function with RAII, and automatically cleans the handle when
// JavaScript context is destroyed, even when the class is not destroyed.
class SafeV8Function {
public:
SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value);
SafeV8Function(const SafeV8Function& other);
bool is_alive() const { return v8_function_.get(); }
v8::Local<v8::Function> NewHandle() const;
private:
void Init();
void FreeHandle();
scoped_refptr<RefCountedPersistent<v8::Function>> v8_function_;
base::WeakPtrFactory<SafeV8Function> weak_factory_;
};
// Helper to invoke a V8 function with C++ parameters.
template <typename Sig>
@ -26,14 +44,17 @@ struct V8FunctionInvoker {};
template <typename... ArgTypes>
struct V8FunctionInvoker<v8::Local<v8::Value>(ArgTypes...)> {
static v8::Local<v8::Value> Go(v8::Isolate* isolate, SafeV8Function function,
static v8::Local<v8::Value> Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::EscapableHandleScope handle_scope(isolate);
if (!function.is_alive())
return v8::Null(isolate);
scoped_ptr<blink::WebScopedRunV8Script> script_scope(
Locker::IsBrowserProcess() ?
nullptr : new blink::WebScopedRunV8Script(isolate));
v8::Local<v8::Function> holder = function->NewHandle();
v8::Local<v8::Function> holder = function.NewHandle();
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args = { ConvertToV8(isolate, raw)... };
@ -44,14 +65,17 @@ struct V8FunctionInvoker<v8::Local<v8::Value>(ArgTypes...)> {
template <typename... ArgTypes>
struct V8FunctionInvoker<void(ArgTypes...)> {
static void Go(v8::Isolate* isolate, SafeV8Function function,
static void Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
if (!function.is_alive())
return;
scoped_ptr<blink::WebScopedRunV8Script> script_scope(
Locker::IsBrowserProcess() ?
nullptr : new blink::WebScopedRunV8Script(isolate));
v8::Local<v8::Function> holder = function->NewHandle();
v8::Local<v8::Function> holder = function.NewHandle();
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args = { ConvertToV8(isolate, raw)... };
@ -61,17 +85,20 @@ struct V8FunctionInvoker<void(ArgTypes...)> {
template <typename ReturnType, typename... ArgTypes>
struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
static ReturnType Go(v8::Isolate* isolate, SafeV8Function function,
static ReturnType Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
ReturnType ret = ReturnType();
if (!function.is_alive())
return ret;
scoped_ptr<blink::WebScopedRunV8Script> script_scope(
Locker::IsBrowserProcess() ?
nullptr : new blink::WebScopedRunV8Script(isolate));
v8::Local<v8::Function> holder = function->NewHandle();
v8::Local<v8::Function> holder = function.NewHandle();
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
ReturnType ret = ReturnType();
std::vector<v8::Local<v8::Value>> args = { ConvertToV8(isolate, raw)... };
v8::Local<v8::Value> result;
auto maybe_result =
@ -119,9 +146,8 @@ struct Converter<base::Callback<Sig>> {
if (!val->IsFunction())
return false;
internal::SafeV8Function function(
new RefCountedPersistent<v8::Function>(isolate, val));
*out = base::Bind(&internal::V8FunctionInvoker<Sig>::Go, isolate, function);
*out = base::Bind(&internal::V8FunctionInvoker<Sig>::Go,
isolate, internal::SafeV8Function(isolate, val));
return true;
}
};