refactor: Clean up the implementation of the registerStreamProtocol (#11357)

* Use weak pointer to avoid race condition

* Use DeleteSoon to delete pointer across threads

* Simplify EventSubscriber

* No need to manually mange V8 convertions

* Fix cpplint warning

We should update cpplint for this, but let's do it in other PR.

* Move UI thread operations to EventSubscriber

* Less and more assertions

Some methods are now private so no more need to assert threads.

* Fix cpplint warnings

* No longer needs the EventEmitted

* EventSubscriber => StreamSubscriber

* Reduce the copies when passing data

* Fix cpplint warnings
This commit is contained in:
Cheng Zhao 2018-10-04 23:13:09 +09:00 committed by John Kleinschmidt
parent 3805c5f538
commit d3ae541397
9 changed files with 314 additions and 373 deletions

View file

@ -4,6 +4,8 @@
#include "atom/common/native_mate_converters/callback.h"
#include "native_mate/dictionary.h"
using content::BrowserThread;
namespace mate {
@ -13,6 +15,22 @@ namespace internal {
namespace {
struct TranslaterHolder {
explicit TranslaterHolder(v8::Isolate* isolate)
: handle(isolate, v8::External::New(isolate, this)) {
handle.SetWeak(this, &GC, v8::WeakCallbackType::kFinalizer);
}
~TranslaterHolder() {
if (!handle.IsEmpty()) {
handle.ClearWeak();
handle.Reset();
}
}
static void GC(const v8::WeakCallbackInfo<TranslaterHolder>& data) {
delete data.GetParameter();
}
v8::Global<v8::External> handle;
Translater translater;
};
@ -22,20 +40,27 @@ v8::Persistent<v8::FunctionTemplate> g_call_translater;
void CallTranslater(v8::Local<v8::External> external,
v8::Local<v8::Object> state,
mate::Arguments* args) {
// Whether the callback should only be called for once.
v8::Isolate* isolate = args->isolate();
bool one_time = state->Has(mate::StringToSymbol(isolate, "oneTime"));
// Check if the callback has already been called.
v8::Local<v8::String> called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(called_symbol)) {
args->ThrowError("callback can only be called for once");
return;
} else {
state->Set(called_symbol, v8::Boolean::New(isolate, true));
if (one_time) {
auto called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(called_symbol)) {
args->ThrowError("callback can only be called for once");
return;
} else {
state->Set(called_symbol, v8::Boolean::New(isolate, true));
}
}
TranslaterHolder* holder = static_cast<TranslaterHolder*>(external->Value());
holder->translater.Run(args);
delete holder;
// Free immediately for one-time callback.
if (one_time)
delete holder;
}
} // namespace
@ -90,8 +115,7 @@ v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
}
v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Isolate* isolate,
const Translater& translater) {
v8::Isolate* isolate, const Translater& translater, bool one_time) {
// The FunctionTemplate is cached.
if (g_call_translater.IsEmpty())
g_call_translater.Reset(isolate, mate::CreateFunctionTemplate(
@ -99,11 +123,16 @@ v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Local<v8::FunctionTemplate> call_translater =
v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
auto* holder = new TranslaterHolder;
auto* holder = new TranslaterHolder(isolate);
holder->translater = translater;
return BindFunctionWith(
isolate, isolate->GetCurrentContext(), call_translater->GetFunction(),
v8::External::New(isolate, holder), v8::Object::New(isolate));
Dictionary state = mate::Dictionary::CreateEmpty(isolate);
if (one_time)
state.Set("oneTime", true);
return BindFunctionWith(isolate,
isolate->GetCurrentContext(),
call_translater->GetFunction(),
holder->handle.Get(isolate),
state.GetHandle());
}
// func.bind(func, arg1).

View file

@ -108,8 +108,8 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
// Helper to pass a C++ funtion to JavaScript.
using Translater = base::Callback<void(Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater);
v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Isolate* isolate, const Translater& translater, bool one_time);
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
@ -152,9 +152,11 @@ struct Converter<base::RepeatingCallback<Sig>> {
const base::RepeatingCallback<Sig>& val) {
// We don't use CreateFunctionTemplate here because it creates a new
// FunctionTemplate everytime, which is cached by V8 and causes leaks.
internal::Translater translater =
base::BindRepeating(&internal::NativeFunctionInvoker<Sig>::Go, val);
return internal::CreateFunctionFromTranslater(isolate, translater);
internal::Translater translater = base::Bind(
&internal::NativeFunctionInvoker<Sig>::Go, val);
// To avoid memory leak, we ensure that the callback can only be called
// for once.
return internal::CreateFunctionFromTranslater(isolate, translater, true);
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
@ -168,6 +170,16 @@ struct Converter<base::RepeatingCallback<Sig>> {
}
};
// Convert a callback to V8 without the call number limitation, this can easily
// cause memory leaks so use it with caution.
template<typename Sig>
v8::Local<v8::Value> CallbackToV8(v8::Isolate* isolate,
const base::Callback<Sig>& val) {
internal::Translater translater = base::Bind(
&internal::NativeFunctionInvoker<Sig>::Go, val);
return internal::CreateFunctionFromTranslater(isolate, translater, false);
}
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CALLBACK_H_