2018-03-31 03:30:30 +08:00
|
|
|
import * as cluster from 'cluster';
|
|
|
|
import {Logger} from '../../Logger';
|
|
|
|
import {DiskManagerTask, ThumbnailTask, WorkerMessage, WorkerTask, WorkerTaskTypes} from './Worker';
|
|
|
|
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
|
2018-07-29 03:27:05 +08:00
|
|
|
import {RendererInput} from './ThumbnailWorker';
|
2018-03-31 03:30:30 +08:00
|
|
|
import {Config} from '../../../common/config/private/Config';
|
2018-12-09 01:17:33 +08:00
|
|
|
import {TaskQue, TaskQueEntry} from './TaskQue';
|
|
|
|
import {ITaskExecuter} from './TaskExecuter';
|
2017-07-04 16:24:20 +08:00
|
|
|
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
interface WorkerWrapper<O> {
|
2017-07-04 16:24:20 +08:00
|
|
|
worker: cluster.Worker;
|
2018-12-09 01:17:33 +08:00
|
|
|
poolTask: TaskQueEntry<WorkerTask, O>;
|
2017-07-04 16:24:20 +08:00
|
|
|
}
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
export class ThreadPool<O> {
|
2017-07-04 16:24:20 +08:00
|
|
|
|
|
|
|
public static WorkerCount = 0;
|
2018-12-09 01:17:33 +08:00
|
|
|
private workers: WorkerWrapper<O>[] = [];
|
|
|
|
private taskQue = new TaskQue<WorkerTask, O>();
|
2017-07-04 16:24:20 +08:00
|
|
|
|
|
|
|
constructor(private size: number) {
|
2018-03-31 03:30:30 +08:00
|
|
|
Logger.silly('Creating thread pool with', size, 'workers');
|
2017-07-04 16:24:20 +08:00
|
|
|
for (let i = 0; i < size; i++) {
|
|
|
|
this.startWorker();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
private run = () => {
|
2018-12-09 01:17:33 +08:00
|
|
|
if (this.taskQue.isEmpty()) {
|
2018-05-13 00:19:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const worker = this.getFreeWorker();
|
|
|
|
if (worker == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
const poolTask = this.taskQue.get();
|
2018-05-13 00:19:51 +08:00
|
|
|
worker.poolTask = poolTask;
|
2018-12-09 01:17:33 +08:00
|
|
|
worker.worker.send(poolTask.data);
|
2018-05-13 00:19:51 +08:00
|
|
|
};
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
protected executeTask(task: WorkerTask): Promise<O> {
|
|
|
|
const promise = this.taskQue.add(task).promise.obj;
|
|
|
|
this.run();
|
|
|
|
return promise;
|
2018-05-13 00:19:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private getFreeWorker() {
|
|
|
|
for (let i = 0; i < this.workers.length; i++) {
|
|
|
|
if (this.workers[i].poolTask == null) {
|
|
|
|
return this.workers[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-07-04 16:24:20 +08:00
|
|
|
private startWorker() {
|
2018-12-09 01:17:33 +08:00
|
|
|
const worker = <WorkerWrapper<O>>{poolTask: null, worker: cluster.fork()};
|
2017-07-04 16:24:20 +08:00
|
|
|
this.workers.push(worker);
|
|
|
|
worker.worker.on('online', () => {
|
|
|
|
ThreadPool.WorkerCount++;
|
|
|
|
Logger.debug('Worker ' + worker.worker.process.pid + ' is online, worker count:', ThreadPool.WorkerCount);
|
|
|
|
});
|
|
|
|
worker.worker.on('exit', (code, signal) => {
|
|
|
|
ThreadPool.WorkerCount--;
|
2018-05-13 00:19:51 +08:00
|
|
|
Logger.warn('Worker ' + worker.worker.process.pid + ' died with code: ' + code +
|
|
|
|
', and signal: ' + signal + ', worker count:', ThreadPool.WorkerCount);
|
2017-07-04 16:24:20 +08:00
|
|
|
Logger.debug('Starting a new worker');
|
|
|
|
this.startWorker();
|
|
|
|
});
|
|
|
|
|
2018-03-31 03:30:30 +08:00
|
|
|
worker.worker.on('message', (msg: WorkerMessage) => {
|
2017-07-04 16:24:20 +08:00
|
|
|
if (worker.poolTask == null) {
|
2018-05-13 00:19:51 +08:00
|
|
|
throw new Error('No worker task after worker task is completed');
|
2017-07-04 16:24:20 +08:00
|
|
|
}
|
|
|
|
if (msg.error) {
|
|
|
|
worker.poolTask.promise.reject(msg.error);
|
|
|
|
} else {
|
|
|
|
worker.poolTask.promise.resolve(msg.result);
|
|
|
|
}
|
2018-12-09 01:17:33 +08:00
|
|
|
this.taskQue.ready(worker.poolTask);
|
2017-07-04 16:24:20 +08:00
|
|
|
worker.poolTask = null;
|
|
|
|
this.run();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
export class DiskManagerTH extends ThreadPool<DirectoryDTO> implements ITaskExecuter<string, DirectoryDTO> {
|
2017-07-04 16:24:20 +08:00
|
|
|
execute(relativeDirectoryName: string): Promise<DirectoryDTO> {
|
|
|
|
return super.executeTask(<DiskManagerTask>{
|
|
|
|
type: WorkerTaskTypes.diskManager,
|
|
|
|
relativeDirectoryName: relativeDirectoryName
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-09 01:17:33 +08:00
|
|
|
export class ThumbnailTH extends ThreadPool<void> implements ITaskExecuter<RendererInput, void> {
|
2017-07-04 16:24:20 +08:00
|
|
|
execute(input: RendererInput): Promise<void> {
|
|
|
|
return super.executeTask(<ThumbnailTask>{
|
|
|
|
type: WorkerTaskTypes.thumbnail,
|
|
|
|
input: input,
|
|
|
|
renderer: Config.Server.thumbnail.processingLibrary
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|