[node] Allow GC in spite of reference loops through the request method

The following produces a reference cycle:

var map = new mbgl.Map({
    request: function () { ... }
});

map has a reference to the request function, which has a reference to
its closure, which has a reference to map.

Storing a the map options in an internal field, rather than a Persistent,
allows V8 to collect the reference cycle when none of its members have
other references.
This commit is contained in:
John Firebaugh
2015-11-20 16:09:58 -08:00
parent 8a0bce7808
commit c4c3a19d85
6 changed files with 35 additions and 73 deletions

View File

@@ -1,8 +1,11 @@
#include "node_map.hpp"
#include "node_request.hpp"
#include "node_mapbox_gl_native.hpp"
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/work_request.hpp>
#include <unistd.h>
@@ -42,7 +45,7 @@ const static char* releasedMessage() {
NAN_MODULE_INIT(NodeMap::Init) {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->InstanceTemplate()->SetInternalFieldCount(2);
tpl->SetClassName(Nan::New("Map").ToLocalChecked());
Nan::SetPrototypeMethod(tpl, "load", Load);
@@ -124,6 +127,8 @@ NAN_METHOD(NodeMap::New) {
return Nan::ThrowError("Options object 'ratio' property must be a number");
}
info.This()->SetInternalField(1, options);
try {
auto nodeMap = new NodeMap(options);
nodeMap->Wrap(info.This());
@@ -427,8 +432,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) :
Nan::HandleScope scope;
return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust() ? Nan::Get(options, Nan::New("ratio").ToLocalChecked()).ToLocalChecked()->NumberValue() : 1.0;
}()),
fs(options),
map(std::make_unique<mbgl::Map>(view, fs, mbgl::MapMode::Still)),
map(std::make_unique<mbgl::Map>(view, *this, mbgl::MapMode::Still)),
async(new uv_async_t) {
async->data = this;
@@ -444,4 +448,27 @@ NodeMap::~NodeMap() {
if (valid) release();
}
class NodeFileSourceRequest : public mbgl::FileRequest {
public:
std::unique_ptr<mbgl::WorkRequest> workRequest;
};
std::unique_ptr<mbgl::FileRequest> NodeMap::request(const mbgl::Resource& resource, Callback cb1) {
auto req = std::make_unique<NodeFileSourceRequest>();
// This function can be called from any thread. Make sure we're executing the
// JS implementation in the node event loop.
req->workRequest = NodeRunLoop().invokeWithCallback([this] (mbgl::Resource res, Callback cb2) {
Nan::HandleScope scope;
auto requestHandle = NodeRequest::Create(res, cb2)->ToObject();
auto callbackHandle = Nan::GetFunction(Nan::New<v8::FunctionTemplate>(NodeRequest::Respond, requestHandle)).ToLocalChecked();
v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
}, cb1, resource);
return std::move(req);
}
}