From 8df3f0a9d9fb9796c093a0e040b13dca8aaa3234 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 23 Aug 2021 23:07:23 +0200 Subject: [PATCH] Use singleton design pattern directly in the worker object factory MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit And remove all now uneeded singleton classes Signed-off-by: Jérôme Benoit --- src/charging-station/Bootstrap.ts | 40 +++++++++++++++--------------- src/worker/WorkerDynamicPool.ts | 23 +++-------------- src/worker/WorkerFactory.ts | 41 +++++++++++++++++++------------ src/worker/WorkerStaticPool.ts | 23 +++-------------- 4 files changed, 53 insertions(+), 74 deletions(-) diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index acfd9914..52e1242f 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -12,11 +12,12 @@ export default class Bootstrap { private version: string = version; private started: boolean; private workerScript: string; - private workerImplementationInstance: WorkerAbstract | null = null; + private workerImplementation: WorkerAbstract | null = null; private constructor() { this.started = false; this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'StationWorker.js'); + this.initWorkerImplementation(); Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart()); } @@ -31,7 +32,7 @@ export default class Bootstrap { if (isMainThread && !this.started) { try { let numStationsTotal = 0; - await this.getWorkerImplementationInstance()?.start(); + await this.workerImplementation.start(); // Start ChargingStation object in worker thread if (Configuration.getStationTemplateURLs()) { for (const stationURL of Configuration.getStationTemplateURLs()) { @@ -42,7 +43,7 @@ export default class Bootstrap { index, templateFile: path.join(path.resolve(__dirname, '../'), 'assets', 'station-templates', path.basename(stationURL.file)) }; - await this.getWorkerImplementationInstance()?.addElement(workerData); + await this.workerImplementation.addElement(workerData); numStationsTotal++; } } catch (error) { @@ -55,7 +56,7 @@ export default class Bootstrap { if (numStationsTotal === 0) { console.log('No charging station template enabled in configuration, exiting'); } else { - console.log(`Charging station simulator ${this.version} started with ${numStationsTotal.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${this.getWorkerImplementationInstance().size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${this.getWorkerImplementationInstance().maxElementsPerWorker ? ` (${this.getWorkerImplementationInstance().maxElementsPerWorker} charging station(s) per worker)` : ''}`); + console.log(`Charging station simulator ${this.version} started with ${numStationsTotal.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${this.workerImplementation.size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${this.workerImplementation.maxElementsPerWorker ? ` (${this.workerImplementation.maxElementsPerWorker} charging station(s) per worker)` : ''}`); } this.started = true; } catch (error) { @@ -66,31 +67,30 @@ export default class Bootstrap { public async stop(): Promise { if (isMainThread && this.started) { - await this.getWorkerImplementationInstance()?.stop(); - // Nullify to force worker implementation instance creation - this.workerImplementationInstance = null; + await this.workerImplementation.stop(); } this.started = false; } public async restart(): Promise { await this.stop(); + this.initWorkerImplementation(true); await this.start(); } - private getWorkerImplementationInstance(): WorkerAbstract | null { - if (!this.workerImplementationInstance) { - this.workerImplementationInstance = WorkerFactory.getWorkerImplementation(this.workerScript, Configuration.getWorkerProcess(), - { - startDelay: Configuration.getWorkerStartDelay(), - poolMaxSize: Configuration.getWorkerPoolMaxSize(), - poolMinSize: Configuration.getWorkerPoolMinSize(), - elementsPerWorker: Configuration.getChargingStationsPerWorker(), - poolOptions: { - workerChoiceStrategy: Configuration.getWorkerPoolStrategy() - } - }); + private initWorkerImplementation(forceInstantiation = false) { + this.workerImplementation = WorkerFactory.getWorkerImplementation(this.workerScript, Configuration.getWorkerProcess(), + { + startDelay: Configuration.getWorkerStartDelay(), + poolMaxSize: Configuration.getWorkerPoolMaxSize(), + poolMinSize: Configuration.getWorkerPoolMinSize(), + elementsPerWorker: Configuration.getChargingStationsPerWorker(), + poolOptions: { + workerChoiceStrategy: Configuration.getWorkerPoolStrategy() + } + }, forceInstantiation); + if (!this.workerImplementation) { + throw new Error('Worker implementation not found'); } - return this.workerImplementationInstance; } } diff --git a/src/worker/WorkerDynamicPool.ts b/src/worker/WorkerDynamicPool.ts index 8ab960f3..306bcd4f 100644 --- a/src/worker/WorkerDynamicPool.ts +++ b/src/worker/WorkerDynamicPool.ts @@ -7,7 +7,7 @@ import { WorkerData } from '../types/Worker'; import { WorkerUtils } from './WorkerUtils'; export default class WorkerDynamicPool extends WorkerAbstract { - private pool: DynamicPool; + private pool: DynamicThreadPool; /** * Create a new `WorkerDynamicPool`. @@ -20,7 +20,8 @@ export default class WorkerDynamicPool extends WorkerAbstract { */ constructor(workerScript: string, min: number, max: number, workerStartDelay?: number, opts?: PoolOptions) { super(workerScript, workerStartDelay); - this.pool = DynamicPool.getInstance(min, max, this.workerScript, opts); + opts.exitHandler = opts?.exitHandler ?? WorkerUtils.defaultExitHandler; + this.pool = new DynamicThreadPool(min, max, this.workerScript, opts); } get size(): number { @@ -37,7 +38,7 @@ export default class WorkerDynamicPool extends WorkerAbstract { * @public */ // eslint-disable-next-line @typescript-eslint/no-empty-function - public async start(): Promise { } + public async start(): Promise {} /** * @@ -61,19 +62,3 @@ export default class WorkerDynamicPool extends WorkerAbstract { await Utils.sleep(this.workerStartDelay); } } - -class DynamicPool extends DynamicThreadPool { - private static instance: DynamicPool; - - private constructor(min: number, max: number, workerScript: string, opts?: PoolOptions) { - super(min, max, workerScript, opts); - } - - public static getInstance(min: number, max: number, workerScript: string, opts?: PoolOptions): DynamicPool { - if (!DynamicPool.instance) { - opts.exitHandler = opts?.exitHandler ?? WorkerUtils.defaultExitHandler; - DynamicPool.instance = new DynamicPool(min, max, workerScript, opts); - } - return DynamicPool.instance; - } -} diff --git a/src/worker/WorkerFactory.ts b/src/worker/WorkerFactory.ts index 3784e7ab..b5cf327c 100644 --- a/src/worker/WorkerFactory.ts +++ b/src/worker/WorkerFactory.ts @@ -8,25 +8,34 @@ import WorkerStaticPool from './WorkerStaticPool'; import { isMainThread } from 'worker_threads'; export default class WorkerFactory { - public static getWorkerImplementation(workerScript: string, workerProcessType: WorkerProcessType, options?: WorkerOptions): WorkerAbstract | null { + private static workerImplementation: WorkerAbstract | null; + + private constructor() {} + + public static getWorkerImplementation(workerScript: string, workerProcessType: WorkerProcessType, options?: WorkerOptions, forceInstantiation = false): WorkerAbstract | null { if (!isMainThread) { throw new Error('Trying to get a worker implementation outside the main thread'); } - options = options ?? {} as WorkerOptions; - options.startDelay = options.startDelay ?? Constants.WORKER_START_DELAY; - switch (workerProcessType) { - case WorkerProcessType.WORKER_SET: - options.elementsPerWorker = options.elementsPerWorker ?? Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; - return new WorkerSet(workerScript, options.elementsPerWorker, options.startDelay); - case WorkerProcessType.STATIC_POOL: - options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; - return new WorkerStaticPool(workerScript, options.poolMaxSize, options.startDelay, options.poolOptions); - case WorkerProcessType.DYNAMIC_POOL: - options.poolMinSize = options.poolMinSize ?? Constants.DEFAULT_WORKER_POOL_MIN_SIZE; - options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; - return new WorkerDynamicPool(workerScript, options.poolMinSize, options.poolMaxSize, options.startDelay, options.poolOptions); - default: - return null; + if (!WorkerFactory.workerImplementation || forceInstantiation) { + options = options ?? {} as WorkerOptions; + options.startDelay = options.startDelay ?? Constants.WORKER_START_DELAY; + WorkerFactory.workerImplementation = null; + switch (workerProcessType) { + case WorkerProcessType.WORKER_SET: + options.elementsPerWorker = options.elementsPerWorker ?? Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; + WorkerFactory.workerImplementation = new WorkerSet(workerScript, options.elementsPerWorker, options.startDelay); + break; + case WorkerProcessType.STATIC_POOL: + options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; + WorkerFactory.workerImplementation = new WorkerStaticPool(workerScript, options.poolMaxSize, options.startDelay, options.poolOptions); + break; + case WorkerProcessType.DYNAMIC_POOL: + options.poolMinSize = options.poolMinSize ?? Constants.DEFAULT_WORKER_POOL_MIN_SIZE; + options.poolMaxSize = options.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; + WorkerFactory.workerImplementation = new WorkerDynamicPool(workerScript, options.poolMinSize, options.poolMaxSize, options.startDelay, options.poolOptions); + break; + } } + return WorkerFactory.workerImplementation; } } diff --git a/src/worker/WorkerStaticPool.ts b/src/worker/WorkerStaticPool.ts index cf46cfa5..a7bb1932 100644 --- a/src/worker/WorkerStaticPool.ts +++ b/src/worker/WorkerStaticPool.ts @@ -7,7 +7,7 @@ import { WorkerData } from '../types/Worker'; import { WorkerUtils } from './WorkerUtils'; export default class WorkerStaticPool extends WorkerAbstract { - private pool: StaticPool; + private pool: FixedThreadPool; /** * Create a new `WorkerStaticPool`. @@ -19,7 +19,8 @@ export default class WorkerStaticPool extends WorkerAbstract { */ constructor(workerScript: string, numberOfThreads: number, startWorkerDelay?: number, opts?: PoolOptions) { super(workerScript, startWorkerDelay); - this.pool = StaticPool.getInstance(numberOfThreads, this.workerScript, opts); + opts.exitHandler = opts?.exitHandler ?? WorkerUtils.defaultExitHandler; + this.pool = new FixedThreadPool(numberOfThreads, this.workerScript, opts); } get size(): number { @@ -36,7 +37,7 @@ export default class WorkerStaticPool extends WorkerAbstract { * @public */ // eslint-disable-next-line @typescript-eslint/no-empty-function - public async start(): Promise { } + public async start(): Promise {} /** * @@ -59,19 +60,3 @@ export default class WorkerStaticPool extends WorkerAbstract { await Utils.sleep(this.workerStartDelay); } } - -class StaticPool extends FixedThreadPool { - private static instance: StaticPool; - - private constructor(numberOfThreads: number, workerScript: string, opts?: PoolOptions) { - super(numberOfThreads, workerScript, opts); - } - - public static getInstance(numberOfThreads: number, workerScript: string, opts?: PoolOptions): StaticPool { - if (!StaticPool.instance) { - opts.exitHandler = opts?.exitHandler ?? WorkerUtils.defaultExitHandler; - StaticPool.instance = new StaticPool(numberOfThreads, workerScript, opts); - } - return StaticPool.instance; - } -} -- 2.34.1