[node] Add map.cancel()

Cancels an ongoing rendering.
This commit is contained in:
Thiago Marcos P. Santos
2017-04-10 20:34:39 +03:00
committed by Thiago Marcos P. Santos
parent d767545f72
commit 51ebb96b1d
5 changed files with 167 additions and 11 deletions

View File

@@ -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());

View File

@@ -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>);

View File

@@ -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);

View 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();
});
})

View File

@@ -107,6 +107,7 @@ test('Map', function(t) {
'loaded',
'render',
'release',
'cancel',
'addClass',
'addSource',
'addLayer',