diff options
author | Thiago Marcos P. Santos <thiago@mapbox.com> | 2017-04-10 20:34:39 +0300 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-04-20 18:46:35 +0300 |
commit | 51ebb96b1da5217d6742ba05ea67a66c7787f0da (patch) | |
tree | 1871a944f7abd6bc35db4a8126e704f8981ef87d | |
parent | d767545f72e5a3e05d37e1d71b2c1f9a86522ca9 (diff) | |
download | qtlocation-mapboxgl-51ebb96b1da5217d6742ba05ea67a66c7787f0da.tar.gz |
[node] Add map.cancel()
Cancels an ongoing rendering.
-rw-r--r-- | platform/node/src/node_map.cpp | 37 | ||||
-rw-r--r-- | platform/node/src/node_map.hpp | 2 | ||||
-rw-r--r-- | platform/node/test/benchmark.js | 22 | ||||
-rw-r--r-- | platform/node/test/js/cancel.test.js | 116 | ||||
-rw-r--r-- | platform/node/test/js/map.test.js | 1 |
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', |