[node] Add map.cancel()
Cancels an ongoing rendering.
This commit is contained in:
committed by
Thiago Marcos P. Santos
parent
d767545f72
commit
51ebb96b1d
@@ -57,6 +57,7 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
|
||||
Nan::SetPrototypeMethod(tpl, "loaded", Loaded);
|
||||
Nan::SetPrototypeMethod(tpl, "render", Render);
|
||||
Nan::SetPrototypeMethod(tpl, "release", Release);
|
||||
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
|
||||
|
||||
Nan::SetPrototypeMethod(tpl, "addClass", AddClass);
|
||||
Nan::SetPrototypeMethod(tpl, "addSource", AddSource);
|
||||
@@ -508,6 +509,42 @@ void NodeMap::release() {
|
||||
map.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an ongoing render request. The callback will be called with
|
||||
* the error set to "Canceled". Will throw if no rendering is in progress.
|
||||
* @name cancel
|
||||
* @returns {undefined}
|
||||
*/
|
||||
void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
|
||||
|
||||
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
|
||||
if (!nodeMap->callback) return Nan::ThrowError("No render in progress");
|
||||
|
||||
try {
|
||||
nodeMap->cancel();
|
||||
} catch (const std::exception &ex) {
|
||||
return Nan::ThrowError(ex.what());
|
||||
}
|
||||
|
||||
info.GetReturnValue().SetUndefined();
|
||||
}
|
||||
|
||||
void NodeMap::cancel() {
|
||||
auto style = map->getStyleJSON();
|
||||
|
||||
map = std::make_unique<mbgl::Map>(backend, mbgl::Size{ 256, 256 },
|
||||
pixelRatio, *this, threadpool, mbgl::MapMode::Still);
|
||||
|
||||
// FIXME: Reload the style after recreating the map. We need to find
|
||||
// a better way of canceling an ongoing rendering on the core level
|
||||
// without resetting the map, which is way too expensive.
|
||||
map->setStyleJSON(style);
|
||||
|
||||
error = std::make_exception_ptr(std::runtime_error("Canceled"));
|
||||
renderFinished();
|
||||
}
|
||||
|
||||
void NodeMap::AddClass(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
|
||||
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
static void Loaded(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void Render(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void AddClass(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
void renderFinished();
|
||||
|
||||
void release();
|
||||
void cancel();
|
||||
|
||||
static RenderOptions ParseOptions(v8::Local<v8::Object>);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ var firstRequest = "mapbox://sprites/mapbox/streets-v9@2x.json";
|
||||
var params = {
|
||||
mapPoolSize: 10,
|
||||
numRenderings: 1000,
|
||||
failurePercentage: 0,
|
||||
timeoutPercentage: 0,
|
||||
failurePercentage: 10,
|
||||
timeoutPercentage: 10,
|
||||
renderingTimeout: 5000,
|
||||
ratio: 2
|
||||
};
|
||||
@@ -20,7 +20,7 @@ test('Benchmark', function(t) {
|
||||
|
||||
var renderCount = 0;
|
||||
var failureCount = 0;
|
||||
var timeoutCount = 0;
|
||||
var cancelCount = 0;
|
||||
|
||||
var options = {
|
||||
request: function(req, callback) {
|
||||
@@ -70,15 +70,13 @@ test('Benchmark', function(t) {
|
||||
t.end();
|
||||
console.timeEnd('Time');
|
||||
console.log('Failures: ' + failureCount);
|
||||
console.log('Timeouts: ' + timeoutCount);
|
||||
console.log('Canceled: ' + cancelCount);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var mapTimeout = setTimeout(function() {
|
||||
map.release();
|
||||
mapPool.push(new mbgl.Map(options));
|
||||
timeoutCount += 1;
|
||||
map.cancel();
|
||||
}, params.renderingTimeout);
|
||||
|
||||
map.render({ zoom: 16 }, function(err, pixels) {
|
||||
@@ -89,13 +87,15 @@ test('Benchmark', function(t) {
|
||||
failureCount += 1;
|
||||
}
|
||||
|
||||
if (err.message == 'Canceled') {
|
||||
cancelCount += 1;
|
||||
}
|
||||
|
||||
// We cancel the request before it gets a
|
||||
// timeout error from the file source.
|
||||
if (err.message == 'Timeout') {
|
||||
t.fail('should never happen');
|
||||
}
|
||||
|
||||
map.release();
|
||||
mapPool.push(new mbgl.Map(options));
|
||||
return;
|
||||
}
|
||||
|
||||
mapPool.push(map);
|
||||
|
||||
116
platform/node/test/js/cancel.test.js
Normal file
116
platform/node/test/js/cancel.test.js
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
var mockfs = require('./../mockfs');
|
||||
var mbgl = require('../../index');
|
||||
var test = require('tape');
|
||||
|
||||
var options = {
|
||||
request: function(req, callback) {
|
||||
callback(null, { data: mockfs.dataForRequest(req) });
|
||||
},
|
||||
ratio: 1,
|
||||
};
|
||||
|
||||
test('Cancel', function(t) {
|
||||
t.test('sanity', function(t) {
|
||||
var renderCount = 0;
|
||||
var cancelCount = 0;
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
var renderCallback = function(err, pixels) {
|
||||
if (err) {
|
||||
cancelCount++;
|
||||
} else {
|
||||
renderCount++;
|
||||
}
|
||||
};
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
|
||||
map.render({ zoom: 16 }, renderCallback);
|
||||
map.cancel();
|
||||
|
||||
map.render({ zoom: 16 }, renderCallback);
|
||||
map.cancel();
|
||||
|
||||
map.render({ zoom: 16 }, renderCallback);
|
||||
map.cancel();
|
||||
|
||||
t.equal(renderCount, 0);
|
||||
t.equal(cancelCount, 3);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('render after cancel', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
var renderCallback = function(err, pixels) { if (!err) t.end(); };
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
|
||||
map.render({ zoom: 16 }, renderCallback);
|
||||
map.cancel();
|
||||
|
||||
map.render({ zoom: 16 }, renderCallback);
|
||||
});
|
||||
|
||||
t.test('cancel after cancel', function(t) {
|
||||
var cancelCount = 0;
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
map.render({ zoom: 16 }, function(err, pixels) {
|
||||
cancelCount++;
|
||||
});
|
||||
|
||||
map.cancel();
|
||||
|
||||
t.throws(function() {
|
||||
map.cancel();
|
||||
}, 'already canceled');
|
||||
|
||||
t.equal(cancelCount, 1);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('cancel without rendering', function(t) {
|
||||
var cancelCount = 0;
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
|
||||
t.throws(function() {
|
||||
map.cancel();
|
||||
}, 'nothing to cancel');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('release after cancel', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
map.render({ zoom: 16 }, function(err, pixels) {});
|
||||
|
||||
map.cancel();
|
||||
map.release();
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('cancel after release', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
map.load(mockfs.style_vector);
|
||||
map.render({ zoom: 16 }, function(err, pixels) {});
|
||||
|
||||
map.release();
|
||||
|
||||
t.throws(function() {
|
||||
map.cancel();
|
||||
}, 'map resources already released');
|
||||
|
||||
t.end();
|
||||
});
|
||||
})
|
||||
@@ -107,6 +107,7 @@ test('Map', function(t) {
|
||||
'loaded',
|
||||
'render',
|
||||
'release',
|
||||
'cancel',
|
||||
'addClass',
|
||||
'addSource',
|
||||
'addLayer',
|
||||
|
||||
Reference in New Issue
Block a user