refactor(simulator): switch to named exports
[e-mobility-charging-stations-simulator.git] / src / worker / WorkerSet.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
c8eeb62b 2
8114d10e 3import { Worker } from 'worker_threads';
c045d9a9 4
268a74bb 5import { WorkerAbstract } from './WorkerAbstract';
0e4fa348
JB
6import {
7 type MessageHandler,
8 type WorkerData,
9 WorkerMessageEvents,
10 type WorkerOptions,
11 type WorkerSetElement,
268a74bb
JB
12} from './WorkerTypes';
13import { WorkerUtils } from './WorkerUtils';
6013bc53 14
268a74bb 15export class WorkerSet extends WorkerAbstract<WorkerData> {
f2bf9948 16 private readonly workerSet: Set<WorkerSetElement>;
6013bc53
JB
17
18 /**
19 * Create a new `WorkerSet`.
20 *
0e4fa348
JB
21 * @param workerScript -
22 * @param workerOptions -
6013bc53 23 */
4d7227e6
JB
24 constructor(workerScript: string, workerOptions?: WorkerOptions) {
25 super(workerScript, workerOptions);
ffd71f2c 26 this.workerSet = new Set<WorkerSetElement>();
6013bc53
JB
27 }
28
29 get size(): number {
ded13d97 30 return this.workerSet.size;
6013bc53
JB
31 }
32
72092cfc 33 get maxElementsPerWorker(): number | undefined {
4d7227e6
JB
34 return this.workerOptions.elementsPerWorker;
35 }
36
6013bc53
JB
37 /**
38 *
0e4fa348 39 * @param elementData -
81797102 40 * @returns
6013bc53
JB
41 * @public
42 */
c3ee95af 43 public async addElement(elementData: WorkerData): Promise<void> {
ded13d97 44 if (!this.workerSet) {
e7aeea18 45 throw new Error("Cannot add a WorkerSet element: workers' set does not exist");
6013bc53 46 }
e7aeea18 47 if (
c1cee602 48 this.workerSet.size === 0 ||
e7aeea18
JB
49 this.getLastWorkerSetElement().numberOfWorkerElements >= this.workerOptions.elementsPerWorker
50 ) {
4581acf3 51 await this.startWorker();
6013bc53 52 }
d070d967
JB
53 this.getLastWorker().postMessage({
54 id: WorkerMessageEvents.START_WORKER_ELEMENT,
e7aeea18 55 data: elementData,
d070d967 56 });
c045d9a9 57 this.getLastWorkerSetElement().numberOfWorkerElements++;
d070d967
JB
58 // Start element sequentially to optimize memory at startup
59 if (this.workerOptions.elementStartDelay > 0) {
268a74bb 60 await WorkerUtils.sleep(this.workerOptions.elementStartDelay);
d070d967 61 }
6013bc53
JB
62 }
63
64 /**
65 *
81797102 66 * @returns
6013bc53
JB
67 * @public
68 */
69 public async start(): Promise<void> {
4581acf3 70 await this.startWorker();
6013bc53
JB
71 }
72
ded13d97
JB
73 /**
74 *
81797102 75 * @returns
ded13d97
JB
76 * @public
77 */
78 public async stop(): Promise<void> {
79 for (const workerSetElement of this.workerSet) {
80 await workerSetElement.worker.terminate();
81 }
82 this.workerSet.clear();
83 }
84
6013bc53 85 /**
1f7a0346 86 * Start a new `Worker`.
6013bc53 87 */
4581acf3 88 private async startWorker(): Promise<void> {
a4624c96 89 const worker = new Worker(this.workerScript);
0e4fa348
JB
90 worker.on(
91 'message',
92 (
93 this.workerOptions?.messageHandler ??
94 (() => {
95 /* This is intentional */
96 })
97 ).bind(this) as MessageHandler<Worker>
98 );
99 worker.on('error', WorkerUtils.defaultErrorHandler.bind(this) as (err: Error) => void);
72092cfc 100 worker.on('exit', (code) => {
7874b0b1 101 WorkerUtils.defaultExitHandler(code);
d7a48614 102 this.workerSet.delete(this.getWorkerSetElementByWorker(worker));
6013bc53 103 });
ded13d97 104 this.workerSet.add({ worker, numberOfWorkerElements: 0 });
4581acf3 105 // Start worker sequentially to optimize memory at startup
e7aeea18 106 this.workerOptions.workerStartDelay > 0 &&
268a74bb 107 (await WorkerUtils.sleep(this.workerOptions.workerStartDelay));
6013bc53
JB
108 }
109
c045d9a9
JB
110 private getLastWorkerSetElement(): WorkerSetElement {
111 let workerSetElement: WorkerSetElement;
e7aeea18
JB
112 for (workerSetElement of this.workerSet) {
113 /* This is intentional */
114 }
c045d9a9
JB
115 return workerSetElement;
116 }
117
118 private getLastWorker(): Worker {
119 return this.getLastWorkerSetElement().worker;
6013bc53 120 }
1e924543
JB
121
122 private getWorkerSetElementByWorker(worker: Worker): WorkerSetElement {
123 let workerSetElt: WorkerSetElement;
0e7a11e1 124 for (const workerSetElement of this.workerSet) {
81696bd5 125 if (workerSetElement.worker.threadId === worker.threadId) {
1e924543 126 workerSetElt = workerSetElement;
0e7a11e1 127 break;
1e924543 128 }
0e7a11e1 129 }
1e924543
JB
130 return workerSetElt;
131 }
6013bc53 132}