summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Marcos P. Santos <thiago@mapbox.com>2017-04-10 20:34:39 +0300
committerThiago Marcos P. Santos <tmpsantos@gmail.com>2017-04-20 18:46:35 +0300
commit51ebb96b1da5217d6742ba05ea67a66c7787f0da (patch)
tree1871a944f7abd6bc35db4a8126e704f8981ef87d
parentd767545f72e5a3e05d37e1d71b2c1f9a86522ca9 (diff)
downloadqtlocation-mapboxgl-51ebb96b1da5217d6742ba05ea67a66c7787f0da.tar.gz
[node] Add map.cancel()
Cancels an ongoing rendering.
-rw-r--r--platform/node/src/node_map.cpp37
-rw-r--r--platform/node/src/node_map.hpp2
-rw-r--r--platform/node/test/benchmark.js22
-rw-r--r--platform/node/test/js/cancel.test.js116
-rw-r--r--platform/node/test/js/map.test.js1
5 files changed, 167 insertions, 11 deletions
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 1890529393..6c4b7345f1 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -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());
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index bfc0a7eab6..cdc8f1e51f 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -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>);
diff --git a/platform/node/test/benchmark.js b/platform/node/test/benchmark.js
index 38c416ed34..50f6353bca 100644
--- a/platform/node/test/benchmark.js
+++ b/platform/node/test/benchmark.js
@@ -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);
diff --git a/platform/node/test/js/cancel.test.js b/platform/node/test/js/cancel.test.js
new file mode 100644
index 0000000000..17525fb863
--- /dev/null
+++ b/platform/node/test/js/cancel.test.js
@@ -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();
+ });
+})
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index e5c756adb6..4ab76b937a 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -107,6 +107,7 @@ test('Map', function(t) {
'loaded',
'render',
'release',
+ 'cancel',
'addClass',
'addSource',
'addLayer',