Skip to content

Commit

Permalink
Add methods to mapboxgl namespace to allow preloading of workers. (#9391
Browse files Browse the repository at this point in the history
)

* Basic preload workers

* Switch to hardcoded preloaded workerpool id so its easier to identify when workers have been preloaded

* Add removePreloadedWorkerPool to mapboxgl namespace

* Fix flow and lint errors

* Add documentation and address CR comments.

* Reword docs language to be clearer.

Co-Authored-By: Konstantin Käfer <mail@kkaefer.com>

* More cleaning of language in documentation.

Co-Authored-By: Konstantin Käfer <mail@kkaefer.com>

Co-authored-by: Konstantin Käfer <mail@kkaefer.com>
  • Loading branch information
Arindam Bose and kkaefer committed Apr 3, 2020
1 parent e7deb5c commit c10c618
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 5 deletions.
34 changes: 34 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {Debug} from './util/debug';
import {isSafari} from './util/util';
import {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin';
import WorkerPool from './util/worker_pool';
import {prewarm, clearPrewarmedResources} from './util/global_worker_pool';
import {clearTileCache} from './util/tile_request_cache';
import {PerformanceUtils} from './util/performance';

Expand All @@ -46,6 +47,39 @@ const exported = {
MercatorCoordinate,
Evented,
config,
/**
* Initializes resources like WebWorkers that can be shared across maps to lower load
* times in some situations. `mapboxgl.workerUrl` and `mapboxgl.workerCount`, if being
* used, must be set before `prewarm()` is called to have an effect.
*
* By default, the lifecycle of these resources is managed automatically, and they are
* lazily initialized when a Map is first created. By invoking `prewarm()`, these
* resources will be created ahead of time, and will not be cleared when the last Map
* is removed from the page. This allows them to be re-used by new Map instances that
* are created later. They can be manually cleared by calling
* `mapboxgl.clearPrewarmedResources()`. This is only necessary if your web page remains
* active but stops using maps altogether.
*
* This is primarily useful when using GL-JS maps in a single page app, wherein a user
* would navigate between various views that can cause Map instances to constantly be
* created and destroyed.
*
* @function prewarm
* @example
* mapboxgl.prewarm()
*/
prewarm,
/**
* Clears up resources that have previously been created by `mapboxgl.prewarm()`.
* Note that this is typically not necessary. You should only call this function
* if you expect the user of your app to not return to a Map view at any point
* in your application.
*
* @function clearPrewarmedResources
* @example
* mapboxgl.clearPrewarmedResources()
*/
clearPrewarmedResources,

/**
* Gets and sets the map's [access token](https://www.mapbox.com/help/define-access-token/).
Expand Down
20 changes: 19 additions & 1 deletion src/util/global_worker_pool.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import WorkerPool from './worker_pool';
import WorkerPool, {PRELOAD_POOL_ID} from './worker_pool';

let globalWorkerPool;

Expand All @@ -15,3 +15,21 @@ export default function getGlobalWorkerPool () {
}
return globalWorkerPool;
}

export function prewarm() {
const workerPool = getGlobalWorkerPool();
workerPool.acquire(PRELOAD_POOL_ID);
}

export function clearPrewarmedResources() {
const pool = globalWorkerPool;
if (pool) {
// Remove the pool only if all maps that referenced the preloaded global worker pool have been removed.
if (pool.isPreloaded() && pool.numActive() === 1) {
pool.release(PRELOAD_POOL_ID);
globalWorkerPool = null;
} else {
console.warn('Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()');
}
}
}
18 changes: 14 additions & 4 deletions src/util/worker_pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ import WebWorker from './web_worker';
import type {WorkerInterface} from './web_worker';
import browser from './browser';

export const PRELOAD_POOL_ID = 'mapboxgl_preloaded_worker_pool';

/**
* Constructs a worker pool.
* @private
*/
export default class WorkerPool {
static workerCount: number;

active: {[_: number]: boolean};
active: {[_: number | string]: boolean};
workers: Array<WorkerInterface>;

constructor() {
this.active = {};
}

acquire(mapId: number): Array<WorkerInterface> {
acquire(mapId: number | string): Array<WorkerInterface> {
if (!this.workers) {
// Lazily look up the value of mapboxgl.workerCount so that
// client code has had a chance to set it.
Expand All @@ -32,15 +34,23 @@ export default class WorkerPool {
return this.workers.slice();
}

release(mapId: number) {
release(mapId: number | string) {
delete this.active[mapId];
if (Object.keys(this.active).length === 0) {
if (this.numActive() === 0) {
this.workers.forEach((w) => {
w.terminate();
});
this.workers = (null: any);
}
}

isPreloaded(): boolean {
return !!this.active[PRELOAD_POOL_ID];
}

numActive(): number {
return Object.keys(this.active).length;
}
}

const availableLogicalProcessors = Math.floor(browser.hardwareConcurrency / 2);
Expand Down

0 comments on commit c10c618

Please sign in to comment.