diff --git a/bench/benchmarks/buffer.js b/bench/benchmarks/buffer.js
index 61d0b13ce9c..0b8f1a8fd61 100644
--- a/bench/benchmarks/buffer.js
+++ b/bench/benchmarks/buffer.js
@@ -91,14 +91,14 @@ function preloadAssets(stylesheet, callback) {
style.on('load', function() {
function getGlyphs(params, callback) {
- style['get glyphs'](params, function(err, glyphs) {
+ style['get glyphs'](0, params, function(err, glyphs) {
assets.glyphs[JSON.stringify(params)] = glyphs;
callback(err, glyphs);
});
}
function getIcons(params, callback) {
- style['get icons'](params, function(err, icons) {
+ style['get icons'](0, params, function(err, icons) {
assets.icons[JSON.stringify(params)] = icons;
callback(err, icons);
});
@@ -185,10 +185,10 @@ var createLayerFamiliesCacheValue;
function createLayerFamilies(layers) {
if (layers !== createLayerFamiliesCacheKey) {
var worker = new Worker({addEventListener: function() {} });
- worker['set layers'](layers);
+ worker['set layers'](0, layers);
createLayerFamiliesCacheKey = layers;
- createLayerFamiliesCacheValue = worker.layerFamilies;
+ createLayerFamiliesCacheValue = worker.layerFamilies[0];
}
return createLayerFamiliesCacheValue;
}
diff --git a/bench/benchmarks/map_load.js b/bench/benchmarks/map_load.js
new file mode 100644
index 00000000000..01de2faefe4
--- /dev/null
+++ b/bench/benchmarks/map_load.js
@@ -0,0 +1,40 @@
+'use strict';
+
+var Evented = require('../../js/util/evented');
+var util = require('../../js/util/util');
+var formatNumber = require('../lib/format_number');
+
+module.exports = function(options) {
+ var evented = util.extend({}, Evented);
+
+ var mapsOnPage = 6;
+
+ evented.fire('log', { message: 'Creating ' + mapsOnPage + ' maps' });
+
+ var loaded = 0;
+ var start = Date.now();
+ for (var i = 0; i < mapsOnPage; i++) {
+ var map = options.createMap({});
+ map.on('load', onload.bind(null, map));
+ map.on('error', function (err) {
+ evented.fire('error', err);
+ });
+ }
+
+ function onload () {
+ if (++loaded >= mapsOnPage) {
+ var duration = Date.now() - start;
+ evented.fire('end', {
+ message: formatNumber(duration) + ' ms, loaded ' + mapsOnPage + ' maps.',
+ score: duration
+ });
+ done();
+ }
+ }
+
+ function done () {
+ }
+
+ return evented;
+};
+
diff --git a/bench/index.js b/bench/index.js
index b6d11c43111..40895d79f7d 100644
--- a/bench/index.js
+++ b/bench/index.js
@@ -198,6 +198,7 @@ var BenchmarksView = React.createClass({
});
var benchmarks = {
+ 'load-multiple-maps': require('./benchmarks/map_load'),
buffer: require('./benchmarks/buffer'),
fps: require('./benchmarks/fps'),
'frame-duration': require('./benchmarks/frame_duration'),
diff --git a/debug/shared_workers.html b/debug/shared_workers.html
new file mode 100644
index 00000000000..e36330e7fa7
--- /dev/null
+++ b/debug/shared_workers.html
@@ -0,0 +1,122 @@
+
+
+
+ Mapbox GL JS debug page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Waiting for data...
+
+
+
+
+
+
+
+
+
diff --git a/js/source/worker.js b/js/source/worker.js
index b79a30747a9..09e0afbea32 100644
--- a/js/source/worker.js
+++ b/js/source/worker.js
@@ -15,29 +15,28 @@ function Worker(self) {
this.self = self;
this.actor = new Actor(self, this);
- // simple accessor object for passing to WorkerSources
- var styleLayers = {
- getLayers: function () { return this.layers; }.bind(this),
- getLayerFamilies: function () { return this.layerFamilies; }.bind(this)
- };
+ this.layers = {};
+ this.layerFamilies = {};
- this.workerSources = {
- vector: new VectorTileWorkerSource(this.actor, styleLayers),
- geojson: new GeoJSONWorkerSource(this.actor, styleLayers)
+ this.workerSourceTypes = {
+ vector: VectorTileWorkerSource,
+ geojson: GeoJSONWorkerSource
};
+ // [mapId][sourceType] => worker source instance
+ this.workerSources = {};
+
this.self.registerWorkerSource = function (name, WorkerSource) {
- if (this.workerSources[name]) {
+ if (this.workerSourceTypes[name]) {
throw new Error('Worker source with name "' + name + '" already registered.');
}
- this.workerSources[name] = new WorkerSource(this.actor, styleLayers);
+ this.workerSourceTypes[name] = WorkerSource;
}.bind(this);
}
util.extend(Worker.prototype, {
- 'set layers': function(layers) {
- this.layers = {};
- var that = this;
+ 'set layers': function(mapId, layers) {
+ var styleLayers = this.layers[mapId] = {};
// Filter layers and create an id -> layer map
var childLayerIndicies = [];
@@ -60,20 +59,21 @@ util.extend(Worker.prototype, {
function setLayer(serializedLayer) {
var styleLayer = StyleLayer.create(
serializedLayer,
- serializedLayer.ref && that.layers[serializedLayer.ref]
+ serializedLayer.ref && styleLayers[serializedLayer.ref]
);
styleLayer.updatePaintTransitions({}, {transition: false});
- that.layers[styleLayer.id] = styleLayer;
+ styleLayers[styleLayer.id] = styleLayer;
}
- this.layerFamilies = createLayerFamilies(this.layers);
+ this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]);
},
- 'update layers': function(layers) {
- var that = this;
+ 'update layers': function(mapId, layers) {
var id;
var layer;
+ var styleLayers = this.layers[mapId];
+
// Update ref parents
for (id in layers) {
layer = layers[id];
@@ -87,41 +87,41 @@ util.extend(Worker.prototype, {
}
function updateLayer(layer) {
- var refLayer = that.layers[layer.ref];
- if (that.layers[layer.id]) {
- that.layers[layer.id].set(layer, refLayer);
+ var refLayer = styleLayers[layer.ref];
+ if (styleLayers[layer.id]) {
+ styleLayers[layer.id].set(layer, refLayer);
} else {
- that.layers[layer.id] = StyleLayer.create(layer, refLayer);
+ styleLayers[layer.id] = StyleLayer.create(layer, refLayer);
}
- that.layers[layer.id].updatePaintTransitions({}, {transition: false});
+ styleLayers[layer.id].updatePaintTransitions({}, {transition: false});
}
- this.layerFamilies = createLayerFamilies(this.layers);
+ this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]);
},
- 'load tile': function(params, callback) {
+ 'load tile': function(mapId, params, callback) {
var type = params.type || 'vector';
- this.workerSources[type].loadTile(params, callback);
+ this.getWorkerSource(mapId, type).loadTile(params, callback);
},
- 'reload tile': function(params, callback) {
+ 'reload tile': function(mapId, params, callback) {
var type = params.type || 'vector';
- this.workerSources[type].reloadTile(params, callback);
+ this.getWorkerSource(mapId, type).reloadTile(params, callback);
},
- 'abort tile': function(params) {
+ 'abort tile': function(mapId, params) {
var type = params.type || 'vector';
- this.workerSources[type].abortTile(params);
+ this.getWorkerSource(mapId, type).abortTile(params);
},
- 'remove tile': function(params) {
+ 'remove tile': function(mapId, params) {
var type = params.type || 'vector';
- this.workerSources[type].removeTile(params);
+ this.getWorkerSource(mapId, type).removeTile(params);
},
- 'redo placement': function(params, callback) {
+ 'redo placement': function(mapId, params, callback) {
var type = params.type || 'vector';
- this.workerSources[type].redoPlacement(params, callback);
+ this.getWorkerSource(mapId, type).redoPlacement(params, callback);
},
/**
@@ -130,13 +130,28 @@ util.extend(Worker.prototype, {
* function taking `(name, workerSourceObject)`.
* @private
*/
- 'load worker source': function(params, callback) {
+ 'load worker source': function(map, params, callback) {
try {
this.self.importScripts(params.url);
callback();
} catch (e) {
callback(e);
}
+ },
+
+ getWorkerSource: function(mapId, type) {
+ if (!this.workerSources[mapId])
+ this.workerSources[mapId] = {};
+ if (!this.workerSources[mapId][type]) {
+ // simple accessor object for passing to WorkerSources
+ var styleLayers = {
+ getLayers: function () { return this.layers[mapId]; }.bind(this),
+ getLayerFamilies: function () { return this.layerFamilies[mapId]; }.bind(this)
+ };
+ this.workerSources[mapId][type] = new this.workerSourceTypes[type](this.actor, styleLayers);
+ }
+
+ return this.workerSources[mapId][type];
}
});
diff --git a/js/style/style.js b/js/style/style.js
index 3e950d14f78..3fb511bbd81 100644
--- a/js/style/style.js
+++ b/js/style/style.js
@@ -19,12 +19,15 @@ var QueryFeatures = require('../source/query_features');
var SourceCache = require('../source/source_cache');
var styleSpec = require('./style_spec');
var StyleFunction = require('./style_function');
+var WorkerPool = require('../util/worker_pool');
+
+var workerPool = new WorkerPool();
module.exports = Style;
function Style(stylesheet, animationLoop, workerCount) {
this.animationLoop = animationLoop || new AnimationLoop();
- this.dispatcher = new Dispatcher(workerCount || 1, this);
+ this.dispatcher = new Dispatcher(workerPool, workerCount || 1, this);
this.spriteAtlas = new SpriteAtlas(1024, 1024);
this.lineAtlas = new LineAtlas(256, 512);
@@ -751,7 +754,7 @@ Style.prototype = util.inherit(Evented, {
// Callbacks from web workers
- 'get sprite json': function(params, callback) {
+ 'get sprite json': function(_, params, callback) {
var sprite = this.sprite;
if (sprite.loaded()) {
callback(null, { sprite: sprite.data, retina: sprite.retina });
@@ -762,7 +765,7 @@ Style.prototype = util.inherit(Evented, {
}
},
- 'get icons': function(params, callback) {
+ 'get icons': function(_, params, callback) {
var sprite = this.sprite;
var spriteAtlas = this.spriteAtlas;
if (sprite.loaded()) {
@@ -776,7 +779,7 @@ Style.prototype = util.inherit(Evented, {
}
},
- 'get glyphs': function(params, callback) {
+ 'get glyphs': function(_, params, callback) {
var stacks = params.stacks,
remaining = Object.keys(stacks).length,
allGlyphs = {};
diff --git a/js/util/actor.js b/js/util/actor.js
index dc09086cfeb..9aefb0298de 100644
--- a/js/util/actor.js
+++ b/js/util/actor.js
@@ -10,11 +10,13 @@ module.exports = Actor;
*
* @param {WebWorker} target
* @param {WebWorker} parent
+ * @param {string|number} mapId A unique identifier for the Map instance using this Actor.
* @private
*/
-function Actor(target, parent) {
+function Actor(target, parent, mapId) {
this.target = target;
this.parent = parent;
+ this.mapId = mapId;
this.callbacks = {};
this.callbackID = 0;
this.receive = this.receive.bind(this);
@@ -32,17 +34,19 @@ Actor.prototype.receive = function(message) {
if (callback) callback(data.error || null, data.data);
} else if (typeof data.id !== 'undefined' && this.parent[data.type]) {
// data.type == 'load tile', 'remove tile', etc.
- this.parent[data.type](data.data, done.bind(this));
- } else if (typeof data.id !== 'undefined' && this.parent.workerSources) {
+ this.parent[data.type](data.mapId, data.data, done.bind(this));
+ } else if (typeof data.id !== 'undefined' && this.parent.getWorkerSource) {
// data.type == sourcetype.method
var keys = data.type.split('.');
- this.parent.workerSources[keys[0]][keys[1]](data.data, done.bind(this));
+ var workerSource = this.parent.getWorkerSource(data.mapId, keys[0]);
+ workerSource[keys[1]](data.data, done.bind(this));
} else {
this.parent[data.type](data.data);
}
function done(err, data, buffers) {
this.postMessage({
+ mapId: this.mapId,
type: '',
id: String(id),
error: err ? String(err) : null,
@@ -52,9 +56,9 @@ Actor.prototype.receive = function(message) {
};
Actor.prototype.send = function(type, data, callback, buffers) {
- var id = null;
- if (callback) this.callbacks[id = this.callbackID++] = callback;
- this.postMessage({ type: type, id: String(id), data: data }, buffers);
+ var id = callback ? this.mapId + ':' + this.callbackID++ : null;
+ if (callback) this.callbacks[id] = callback;
+ this.postMessage({ mapId: this.mapId, type: type, id: String(id), data: data }, buffers);
};
/**
diff --git a/js/util/dispatcher.js b/js/util/dispatcher.js
index 7ca9bd7c367..c12a11724ba 100644
--- a/js/util/dispatcher.js
+++ b/js/util/dispatcher.js
@@ -2,7 +2,6 @@
var util = require('./util');
var Actor = require('./actor');
-var WebWorker = require('./web_worker');
module.exports = Dispatcher;
@@ -13,12 +12,15 @@ module.exports = Dispatcher;
* @interface Dispatcher
* @private
*/
-function Dispatcher(length, parent) {
+function Dispatcher(workerPool, length, parent) {
+ this.workerPool = workerPool;
this.actors = [];
this.currentActor = 0;
- for (var i = 0; i < length; i++) {
- var worker = new WebWorker();
- var actor = new Actor(worker, parent);
+ this.id = util.uniqueId();
+ var workers = this.workerPool.acquire(this.id, length);
+ for (var i = 0; i < workers.length; i++) {
+ var worker = workers[i];
+ var actor = new Actor(worker, parent, this.id);
actor.name = "Worker " + i;
this.actors.push(actor);
}
@@ -65,9 +67,8 @@ Dispatcher.prototype = {
},
remove: function() {
- for (var i = 0; i < this.actors.length; i++) {
- this.actors[i].target.terminate();
- }
+ this.workerPool.release(this.id);
this.actors = [];
}
};
+
diff --git a/js/util/worker_pool.js b/js/util/worker_pool.js
new file mode 100644
index 00000000000..246cf80b1b7
--- /dev/null
+++ b/js/util/worker_pool.js
@@ -0,0 +1,35 @@
+'use strict';
+
+var assert = require('assert');
+var WebWorker = require('./web_worker');
+
+module.exports = WorkerPool;
+
+function WorkerPool() {
+ this.workers = [];
+ this.active = {};
+}
+
+WorkerPool.prototype = {
+ acquire: function (mapId, workerCount) {
+ this._resize(workerCount);
+ this.active[mapId] = workerCount;
+ return this.workers.slice(0, workerCount);
+ },
+
+ release: function (mapId) {
+ delete this.active[mapId];
+ if (Object.keys(this.active).length === 0) {
+ this.workers.forEach(function (w) { w.terminate(); });
+ this.workers = [];
+ }
+ },
+
+ _resize: function (len) {
+ assert(typeof len === 'number');
+ while (this.workers.length < len) {
+ this.workers.push(new WebWorker());
+ }
+ }
+};
+
diff --git a/test/js/source/worker.test.js b/test/js/source/worker.test.js
index 66d04dcc708..45c6c3946c9 100644
--- a/test/js/source/worker.test.js
+++ b/test/js/source/worker.test.js
@@ -26,7 +26,7 @@ test('before', function(t) {
test('load tile', function(t) {
t.test('calls callback on error', function(t) {
var worker = new Worker(_self);
- worker['load tile']({
+ worker['load tile'](0, {
source: 'source',
uid: 0,
url: 'http://localhost:2900/error'
@@ -42,25 +42,25 @@ test('load tile', function(t) {
test('set layers', function(t) {
var worker = new Worker(_self);
- worker['set layers']([
+ worker['set layers'](0, [
{ id: 'one', type: 'circle', paint: { 'circle-color': 'red' } },
{ id: 'two', type: 'circle', paint: { 'circle-color': 'green' } },
{ id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } }
]);
- t.equal(worker.layers.one.id, 'one');
- t.equal(worker.layers.two.id, 'two');
- t.equal(worker.layers.three.id, 'three');
+ t.equal(worker.layers[0].one.id, 'one');
+ t.equal(worker.layers[0].two.id, 'two');
+ t.equal(worker.layers[0].three.id, 'three');
- t.equal(worker.layers.one.getPaintProperty('circle-color'), 'red');
- t.equal(worker.layers.two.getPaintProperty('circle-color'), 'green');
- t.equal(worker.layers.three.getPaintProperty('circle-color'), 'blue');
+ t.equal(worker.layers[0].one.getPaintProperty('circle-color'), 'red');
+ t.equal(worker.layers[0].two.getPaintProperty('circle-color'), 'green');
+ t.equal(worker.layers[0].three.getPaintProperty('circle-color'), 'blue');
- t.equal(worker.layerFamilies.one.length, 1);
- t.equal(worker.layerFamilies.one[0].id, 'one');
- t.equal(worker.layerFamilies.two.length, 2);
- t.equal(worker.layerFamilies.two[0].id, 'two');
- t.equal(worker.layerFamilies.two[1].id, 'three');
+ t.equal(worker.layerFamilies[0].one.length, 1);
+ t.equal(worker.layerFamilies[0].one[0].id, 'one');
+ t.equal(worker.layerFamilies[0].two.length, 2);
+ t.equal(worker.layerFamilies[0].two[0].id, 'two');
+ t.equal(worker.layerFamilies[0].two[1].id, 'three');
t.end();
});
@@ -68,21 +68,21 @@ test('set layers', function(t) {
test('update layers', function(t) {
var worker = new Worker(_self);
- worker['set layers']([
+ worker['set layers'](0, [
{ id: 'one', type: 'circle', paint: { 'circle-color': 'red' } },
{ id: 'two', type: 'circle', paint: { 'circle-color': 'green' } },
{ id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } }
]);
- worker['update layers']({
+ worker['update layers'](0, {
one: { id: 'one', type: 'circle', paint: { 'circle-color': 'cyan' } },
two: { id: 'two', type: 'circle', paint: { 'circle-color': 'magenta' } },
three: { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'yellow' } }
});
- t.equal(worker.layers.one.getPaintProperty('circle-color'), 'cyan');
- t.equal(worker.layers.two.getPaintProperty('circle-color'), 'magenta');
- t.equal(worker.layers.three.getPaintProperty('circle-color'), 'yellow');
+ t.equal(worker.layers[0].one.getPaintProperty('circle-color'), 'cyan');
+ t.equal(worker.layers[0].two.getPaintProperty('circle-color'), 'magenta');
+ t.equal(worker.layers[0].three.getPaintProperty('circle-color'), 'yellow');
t.end();
});
@@ -96,7 +96,46 @@ test('redo placement', function(t) {
};
});
- worker['redo placement']({type: 'test', mapbox: true});
+ worker['redo placement'](0, {type: 'test', mapbox: true});
+});
+
+test('update layers isolates different instances\' data', function(t) {
+ var worker = new Worker(_self);
+
+ worker['set layers'](0, [
+ { id: 'one', type: 'circle', paint: { 'circle-color': 'red' } },
+ { id: 'two', type: 'circle', paint: { 'circle-color': 'green' } },
+ { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } }
+ ]);
+
+ worker['set layers'](1, [
+ { id: 'one', type: 'circle', paint: { 'circle-color': 'red' } },
+ { id: 'two', type: 'circle', paint: { 'circle-color': 'green' } },
+ { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } }
+ ]);
+
+ worker['update layers'](1, {
+ one: { id: 'one', type: 'circle', paint: { 'circle-color': 'cyan' } },
+ two: { id: 'two', type: 'circle', paint: { 'circle-color': 'magenta' } },
+ three: { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'yellow' } }
+ });
+
+ t.equal(worker.layers[0].one.id, 'one');
+ t.equal(worker.layers[0].two.id, 'two');
+ t.equal(worker.layers[0].three.id, 'three');
+
+ t.equal(worker.layers[0].one.getPaintProperty('circle-color'), 'red');
+ t.equal(worker.layers[0].two.getPaintProperty('circle-color'), 'green');
+ t.equal(worker.layers[0].three.getPaintProperty('circle-color'), 'blue');
+
+ t.equal(worker.layerFamilies[0].one.length, 1);
+ t.equal(worker.layerFamilies[0].one[0].id, 'one');
+ t.equal(worker.layerFamilies[0].two.length, 2);
+ t.equal(worker.layerFamilies[0].two[0].id, 'two');
+ t.equal(worker.layerFamilies[0].two[1].id, 'three');
+
+
+ t.end();
});
test('after', function(t) {
diff --git a/test/js/util/actor.test.js b/test/js/util/actor.test.js
new file mode 100644
index 00000000000..df02df69c24
--- /dev/null
+++ b/test/js/util/actor.test.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var test = require('tap').test;
+var proxyquire = require('proxyquire');
+var Actor = require('../../../js/util/actor');
+
+test('Actor', function (t) {
+ t.test('forwards resopnses to correct callback', function (t) {
+ var WebWorker = proxyquire('../../../js/util/web_worker', {
+ '../source/worker': function Worker(self) {
+ this.self = self;
+ this.actor = new Actor(self, this);
+ this.test = function (mapId, params, callback) {
+ setTimeout(callback, 0, null, params);
+ };
+ }
+ });
+
+ var worker = new WebWorker();
+
+ var m1 = new Actor(worker, {}, 'map-1');
+ var m2 = new Actor(worker, {}, 'map-2');
+
+ t.plan(4);
+ m1.send('test', { value: 1729 }, function (err, response) {
+ t.error(err);
+ t.same(response, { value: 1729 });
+ });
+ m2.send('test', { value: 4104 }, function (err, response) {
+ t.error(err);
+ t.same(response, { value: 4104 });
+ });
+ });
+
+ t.end();
+});
diff --git a/test/js/util/dispatcher.test.js b/test/js/util/dispatcher.test.js
new file mode 100644
index 00000000000..bb71257c661
--- /dev/null
+++ b/test/js/util/dispatcher.test.js
@@ -0,0 +1,49 @@
+'use strict';
+
+var test = require('tap').test;
+var proxyquire = require('proxyquire');
+var Dispatcher = require('../../../js/util/dispatcher');
+var WebWorker = require('../../../js/util/web_worker');
+var WorkerPool = require('../../../js/util/worker_pool');
+
+test('Dispatcher', function (t) {
+ t.test('requests and releases workers from pool', function (t) {
+ var dispatcher;
+ var workers = [new WebWorker(), new WebWorker()];
+
+ var releaseCalled = [];
+ var workerPool = {
+ acquire: function (id, count) {
+ t.equal(count, 2);
+ return workers;
+ },
+ release: function (id) {
+ releaseCalled.push(id);
+ }
+ };
+
+ dispatcher = new Dispatcher(workerPool, 2, {});
+ t.same(dispatcher.actors.map(function (actor) { return actor.target; }), workers);
+ dispatcher.remove();
+ t.equal(dispatcher.actors.length, 0, 'actors discarded');
+ t.same(releaseCalled, [dispatcher.id]);
+
+ t.end();
+ });
+
+ test('creates Actors with unique map id', function (t) {
+ var Dispatcher = proxyquire('../../../js/util/dispatcher', { './actor': Actor });
+
+ var ids = [];
+ function Actor (target, parent, mapId) { ids.push(mapId); }
+
+ var workerPool = new WorkerPool();
+ var dispatchers = [new Dispatcher(workerPool, 1, {}), new Dispatcher(workerPool, 1, {})];
+ t.same(ids, dispatchers.map(function (d) { return d.id; }));
+
+ t.end();
+ });
+
+ t.end();
+});
+
diff --git a/test/js/util/worker_pool.test.js b/test/js/util/worker_pool.test.js
new file mode 100644
index 00000000000..f1f085d677c
--- /dev/null
+++ b/test/js/util/worker_pool.test.js
@@ -0,0 +1,46 @@
+'use strict';
+
+var test = require('tap').test;
+var WorkerPool = require('../../../js/util/worker_pool');
+
+test('WorkerPool', function (t) {
+ t.test('#acquire', function (t) {
+ var pool = new WorkerPool();
+
+ t.equal(pool.workers.length, 0);
+ var workers1 = pool.acquire('map-1', 4);
+ t.equal(workers1.length, 4);
+
+ var workers2 = pool.acquire('map-2', 8);
+ t.equal(workers2.length, 8);
+ t.equal(pool.workers.length, 8);
+
+ // check that the two different dispatchers' workers arrays correspond
+ workers1.forEach(function (w, i) { t.equal(w, workers2[i]); });
+ t.end();
+ });
+
+ t.test('#release', function (t) {
+ var pool = new WorkerPool();
+ pool.acquire('map-1', 1);
+ var workers = pool.acquire('map-2', 4);
+ var terminated = 0;
+ workers.forEach(function (w) {
+ w.terminate = function () { terminated += 1; };
+ });
+
+ pool.release('map-2');
+ t.comment('keeps workers if a dispatcher is still active');
+ t.equal(terminated, 0);
+ t.equal(pool.workers.length, 4);
+
+ t.comment('terminates workers if no dispatchers are active');
+ pool.release('map-1');
+ t.equal(terminated, 4);
+ t.equal(pool.workers.length, 0);
+
+ t.end();
+ });
+
+ t.end();
+});