180 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| Object.defineProperty(exports, '__esModule', {
 | |
|   value: true
 | |
| });
 | |
| exports.default = void 0;
 | |
| 
 | |
| var _types = require('./types');
 | |
| 
 | |
| 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, computeWorkerKey) {
 | |
|     _defineProperty(this, '_computeWorkerKey', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_cacheKeys', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_callback', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_last', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_locks', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_numOfWorkers', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_offset', void 0);
 | |
| 
 | |
|     _defineProperty(this, '_queue', void 0);
 | |
| 
 | |
|     this._cacheKeys = Object.create(null);
 | |
|     this._callback = callback;
 | |
|     this._last = [];
 | |
|     this._locks = [];
 | |
|     this._numOfWorkers = numOfWorkers;
 | |
|     this._offset = 0;
 | |
|     this._queue = [];
 | |
| 
 | |
|     if (computeWorkerKey) {
 | |
|       this._computeWorkerKey = computeWorkerKey;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   doWork(method, ...args) {
 | |
|     return new Promise((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) => {
 | |
|         if (error) {
 | |
|           reject(error);
 | |
|         } else {
 | |
|           resolve(result);
 | |
|         }
 | |
|       };
 | |
| 
 | |
|       const task = {
 | |
|         onEnd,
 | |
|         onStart,
 | |
|         request
 | |
|       };
 | |
| 
 | |
|       if (worker) {
 | |
|         this._enqueue(task, worker.getWorkerId());
 | |
|       } else {
 | |
|         this._push(task);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   _getNextTask(workerId) {
 | |
|     let queueHead = this._queue[workerId];
 | |
| 
 | |
|     while (queueHead && queueHead.task.request[1]) {
 | |
|       queueHead = queueHead.next || null;
 | |
|     }
 | |
| 
 | |
|     this._queue[workerId] = queueHead;
 | |
|     return queueHead && queueHead.task;
 | |
|   }
 | |
| 
 | |
|   _process(workerId) {
 | |
|     if (this._isLocked(workerId)) {
 | |
|       return this;
 | |
|     }
 | |
| 
 | |
|     const task = this._getNextTask(workerId);
 | |
| 
 | |
|     if (!task) {
 | |
|       return this;
 | |
|     }
 | |
| 
 | |
|     const onEnd = (error, result) => {
 | |
|       task.onEnd(error, result);
 | |
| 
 | |
|       this._unlock(workerId);
 | |
| 
 | |
|       this._process(workerId);
 | |
|     };
 | |
| 
 | |
|     task.request[1] = true;
 | |
| 
 | |
|     this._lock(workerId);
 | |
| 
 | |
|     this._callback(workerId, task.request, task.onStart, onEnd);
 | |
| 
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   _enqueue(task, workerId) {
 | |
|     const item = {
 | |
|       next: null,
 | |
|       task
 | |
|     };
 | |
| 
 | |
|     if (task.request[1]) {
 | |
|       return this;
 | |
|     }
 | |
| 
 | |
|     if (this._queue[workerId]) {
 | |
|       this._last[workerId].next = item;
 | |
|     } else {
 | |
|       this._queue[workerId] = item;
 | |
|     }
 | |
| 
 | |
|     this._last[workerId] = item;
 | |
| 
 | |
|     this._process(workerId);
 | |
| 
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   _push(task) {
 | |
|     for (let i = 0; i < this._numOfWorkers; i++) {
 | |
|       this._enqueue(task, (this._offset + i) % this._numOfWorkers);
 | |
|     }
 | |
| 
 | |
|     this._offset++;
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   _lock(workerId) {
 | |
|     this._locks[workerId] = true;
 | |
|   }
 | |
| 
 | |
|   _unlock(workerId) {
 | |
|     this._locks[workerId] = false;
 | |
|   }
 | |
| 
 | |
|   _isLocked(workerId) {
 | |
|     return this._locks[workerId];
 | |
|   }
 | |
| }
 | |
| 
 | |
| exports.default = Farm;
 |