You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
4.7 KiB
206 lines
4.7 KiB
'use strict'; |
|
|
|
Object.defineProperty(exports, '__esModule', { |
|
value: true |
|
}); |
|
exports.default = void 0; |
|
|
|
var _FifoQueue = _interopRequireDefault(require('./FifoQueue')); |
|
|
|
var _types = require('./types'); |
|
|
|
function _interopRequireDefault(obj) { |
|
return obj && obj.__esModule ? obj : {default: obj}; |
|
} |
|
|
|
function _defineProperty(obj, key, value) { |
|
if (key in obj) { |
|
Object.defineProperty(obj, key, { |
|
value: value, |
|
enumerable: true, |
|
configurable: true, |
|
writable: true |
|
}); |
|
} else { |
|
obj[key] = value; |
|
} |
|
return obj; |
|
} |
|
|
|
class Farm { |
|
constructor(_numOfWorkers, _callback, options = {}) { |
|
var _options$workerSchedu, _options$taskQueue; |
|
|
|
_defineProperty(this, '_computeWorkerKey', void 0); |
|
|
|
_defineProperty(this, '_workerSchedulingPolicy', void 0); |
|
|
|
_defineProperty(this, '_cacheKeys', Object.create(null)); |
|
|
|
_defineProperty(this, '_locks', []); |
|
|
|
_defineProperty(this, '_offset', 0); |
|
|
|
_defineProperty(this, '_taskQueue', void 0); |
|
|
|
this._numOfWorkers = _numOfWorkers; |
|
this._callback = _callback; |
|
this._computeWorkerKey = options.computeWorkerKey; |
|
this._workerSchedulingPolicy = |
|
(_options$workerSchedu = options.workerSchedulingPolicy) !== null && |
|
_options$workerSchedu !== void 0 |
|
? _options$workerSchedu |
|
: 'round-robin'; |
|
this._taskQueue = |
|
(_options$taskQueue = options.taskQueue) !== null && |
|
_options$taskQueue !== void 0 |
|
? _options$taskQueue |
|
: new _FifoQueue.default(); |
|
} |
|
|
|
doWork(method, ...args) { |
|
const customMessageListeners = new Set(); |
|
|
|
const addCustomMessageListener = listener => { |
|
customMessageListeners.add(listener); |
|
return () => { |
|
customMessageListeners.delete(listener); |
|
}; |
|
}; |
|
|
|
const onCustomMessage = message => { |
|
customMessageListeners.forEach(listener => listener(message)); |
|
}; |
|
|
|
const promise = new Promise( // Bind args to this function so it won't reference to the parent scope. |
|
// This prevents a memory leak in v8, because otherwise the function will |
|
// retaine args for the closure. |
|
((args, resolve, reject) => { |
|
const computeWorkerKey = this._computeWorkerKey; |
|
const request = [_types.CHILD_MESSAGE_CALL, false, method, args]; |
|
let worker = null; |
|
let hash = null; |
|
|
|
if (computeWorkerKey) { |
|
hash = computeWorkerKey.call(this, method, ...args); |
|
worker = hash == null ? null : this._cacheKeys[hash]; |
|
} |
|
|
|
const onStart = worker => { |
|
if (hash != null) { |
|
this._cacheKeys[hash] = worker; |
|
} |
|
}; |
|
|
|
const onEnd = (error, result) => { |
|
customMessageListeners.clear(); |
|
|
|
if (error) { |
|
reject(error); |
|
} else { |
|
resolve(result); |
|
} |
|
}; |
|
|
|
const task = { |
|
onCustomMessage, |
|
onEnd, |
|
onStart, |
|
request |
|
}; |
|
|
|
if (worker) { |
|
this._taskQueue.enqueue(task, worker.getWorkerId()); |
|
|
|
this._process(worker.getWorkerId()); |
|
} else { |
|
this._push(task); |
|
} |
|
}).bind(null, args) |
|
); |
|
promise.UNSTABLE_onCustomMessage = addCustomMessageListener; |
|
return promise; |
|
} |
|
|
|
_process(workerId) { |
|
if (this._isLocked(workerId)) { |
|
return this; |
|
} |
|
|
|
const task = this._taskQueue.dequeue(workerId); |
|
|
|
if (!task) { |
|
return this; |
|
} |
|
|
|
if (task.request[1]) { |
|
throw new Error('Queue implementation returned processed task'); |
|
} // Reference the task object outside so it won't be retained by onEnd, |
|
// and other properties of the task object, such as task.request can be |
|
// garbage collected. |
|
|
|
const taskOnEnd = task.onEnd; |
|
|
|
const onEnd = (error, result) => { |
|
taskOnEnd(error, result); |
|
|
|
this._unlock(workerId); |
|
|
|
this._process(workerId); |
|
}; |
|
|
|
task.request[1] = true; |
|
|
|
this._lock(workerId); |
|
|
|
this._callback( |
|
workerId, |
|
task.request, |
|
task.onStart, |
|
onEnd, |
|
task.onCustomMessage |
|
); |
|
|
|
return this; |
|
} |
|
|
|
_push(task) { |
|
this._taskQueue.enqueue(task); |
|
|
|
const offset = this._getNextWorkerOffset(); |
|
|
|
for (let i = 0; i < this._numOfWorkers; i++) { |
|
this._process((offset + i) % this._numOfWorkers); |
|
|
|
if (task.request[1]) { |
|
break; |
|
} |
|
} |
|
|
|
return this; |
|
} |
|
|
|
_getNextWorkerOffset() { |
|
switch (this._workerSchedulingPolicy) { |
|
case 'in-order': |
|
return 0; |
|
|
|
case 'round-robin': |
|
return this._offset++; |
|
} |
|
} |
|
|
|
_lock(workerId) { |
|
this._locks[workerId] = true; |
|
} |
|
|
|
_unlock(workerId) { |
|
this._locks[workerId] = false; |
|
} |
|
|
|
_isLocked(workerId) { |
|
return this._locks[workerId]; |
|
} |
|
} |
|
|
|
exports.default = Farm;
|
|
|