import { isDefined, Undefinable } from "src/utils/types"; function canTransferArrayBuffer() { if (!isDefined(TaskProcessor._canTransferArrayBuffer)) { var worker = new Worker(getWorkerUrl("Workers/transferTypedArrayTest.js")); worker.postMessage = (worker as any).webkitPostMessage || worker.postMessage var value = 99; var array = new Int8Array([value]); try { // postMessage might fail with a DataCloneError // if transferring array buffers is not supported. worker.postMessage( { array: array, }, [array.buffer] ); } catch (e) { TaskProcessor._canTransferArrayBuffer = false; return TaskProcessor._canTransferArrayBuffer; } var deferred = new Promise(() => { }); worker.onmessage = (event) => { var array = event.data.array; // some versions of Firefox silently fail to transfer typed arrays. // https://bugzilla.mozilla.org/show_bug.cgi?id=841904 // Check to make sure the value round-trips successfully. var result = isDefined(array) && array[0] === value; new Promise((resolve, reject) => { resolve(result); }).then(() => { TaskProcessor._canTransferArrayBuffer = true; }); worker.terminate(); TaskProcessor._canTransferArrayBuffer = result; }; } return TaskProcessor._canTransferArrayBuffer; } function completeTask(processor: any, data: any) { --processor._activeTasks; var id = data.id; if (!isDefined(id)) { // This is not one of ours. return; } var deferreds = processor._deferreds; var deferred = deferreds[id]; if (isDefined(data.error)) { var error = data.error; if (error.name === "RuntimeError") { error = console.log(data.error.message); } else if (error.name === "DeveloperError") { error = console.log(data.error.message); } // taskCompletedEvent.raiseEvent(error); deferred.reject(error); } else { // taskCompletedEvent.raiseEvent(); deferred.resolve(data.result); } delete deferreds[id]; } function getWorkerUrl(moduleID: any) { var url = moduleID; // if (isCrossOriginUrl(url)) { // //to load cross-origin, create a shim worker from a blob URL // var script = 'importScripts("' + url + '");'; // var blob; // try { // blob = new Blob([script], { // type: "application/javascript", // }); // } catch (e) { // var BlobBuilder = // (window as any).BlobBuilder || // (window as any).WebKitBlobBuilder || // (window as any).MozBlobBuilder || // (window as any).MSBlobBuilder; // var blobBuilder = new BlobBuilder(); // blobBuilder.append(script); // blob = blobBuilder.getBlob("application/javascript"); // } // var URL = window.URL || window.webkitURL; // url = URL.createObjectURL(blob); // } return url; } var bootstrapperUrlResult: any; function getBootstrapperUrl() { if (!isDefined(bootstrapperUrlResult)) { bootstrapperUrlResult = getWorkerUrl("Workers/cesiumWorkerBootstrapper.js"); } return bootstrapperUrlResult; } function createWorker(processor: TaskProcessor) { var worker = new Worker(getBootstrapperUrl()); worker.postMessage = (worker).webkitPostMessage || worker.postMessage; var bootstrapMessage = { }; worker.postMessage(bootstrapMessage); worker.onmessage = function (event) { completeTask(processor, event.data); }; return worker; } var emptyTransferableObjectArray: any = []; /** * A wrapper around a web worker that allows scheduling tasks for a given worker, * returning results asynchronously via a promise. * * The Worker is not constructed until a task is scheduled. * * @alias TaskProcessor * @constructor * * @param {String} workerPath The Url to the worker. This can either be an absolute path or relative to the Cesium Workers folder. * @param {Number} [maximumActiveTasks=5] The maximum number of active tasks. Once exceeded, * scheduleTask will not queue any more tasks, allowing * work to be rescheduled in future frames. */ export class TaskProcessor { static _defaultWorkerModulePrefix = "Workers/"; static _workerModulePrefix = TaskProcessor._defaultWorkerModulePrefix; static _canTransferArrayBuffer: boolean = true; private _workerPath: string = ''; private _maximumActiveTasks: number; private _activeTasks: number; _deferreds: any = {}; private _nextID: number; private _worker: Undefinable constructor(workerPath: string, maximumActiveTasks: number = 5) { this._workerPath = TaskProcessor._workerModulePrefix + workerPath; this._maximumActiveTasks = maximumActiveTasks; this._activeTasks = 0; this._deferreds = {}; this._nextID = 0; } scheduleTask(parameters: any, transferableObjects: any) { if (!isDefined(this._worker)) { this._worker = createWorker(this); } if (this._activeTasks >= this._maximumActiveTasks) { return undefined; } ++this._activeTasks; var processor = this; var deferred = new Promise((resolve, reject) => { resolve(canTransferArrayBuffer()) }).then(function (canTransferArrayBuffer) { if (!isDefined(transferableObjects)) { transferableObjects = emptyTransferableObjectArray; } else if (!canTransferArrayBuffer) { transferableObjects.length = 0; } var id = processor._nextID++; processor._deferreds[id] = deferred; if (processor._worker) processor._worker.postMessage( { id: id, parameters: parameters, canTransferArrayBuffer: canTransferArrayBuffer, }, transferableObjects ); }); } isDestroyed() { return false; }; /** * Destroys this object. This will immediately terminate the Worker. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. */ destroy() { if (isDefined(this._worker)) { this._worker!.terminate(); } }; }