Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to mapboxgl namespace to allow preloading of workers. #9391

Merged
merged 8 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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