Commit | Line | Data |
---|---|---|
fa699c42 | 1 | import { EventEmitter } from 'events' |
60fbd6d6 S |
2 | import { |
3 | FixedThreadPool, | |
f045358d S |
4 | FixedThreadPoolOptions, |
5 | WorkerWithMessageChannel | |
6 | } from './fixed' | |
7 | ||
4ade5f1f S |
8 | class MyEmitter extends EventEmitter {} |
9 | ||
10 | export type DynamicThreadPoolOptions = FixedThreadPoolOptions | |
11 | ||
12 | /** | |
13 | * A thread pool with a min/max number of threads, is possible to execute tasks in sync or async mode as you prefer. | |
14 | * | |
15 | * This thread pool will create new workers when the other ones are busy, until the max number of threads, | |
16 | * when the max number of threads is reached, an event will be emitted, if you want to listen this event use the emitter method. | |
17 | * | |
18 | * @author [Alessandro Pio Ardizio](https://github.com/pioardi) | |
19 | * @since 0.0.1 | |
20 | */ | |
777b7824 | 21 | /* eslint-disable @typescript-eslint/no-explicit-any */ |
60fbd6d6 | 22 | export class DynamicThreadPool< |
4ade5f1f S |
23 | Data = any, |
24 | Response = any | |
25 | > extends FixedThreadPool<Data, Response> { | |
777b7824 | 26 | /* eslint-enable @typescript-eslint/no-explicit-any */ |
4ade5f1f S |
27 | public readonly emitter: MyEmitter |
28 | ||
29 | /** | |
30 | * @param min Min number of threads that will be always active | |
31 | * @param max Max number of threads that will be active | |
32 | * @param filename A file path with implementation of `ThreadWorker` class, relative path is fine. | |
33 | * @param opts An object with possible options for example `errorHandler`, `onlineHandler`. Default: `{ maxTasks: 1000 }` | |
34 | */ | |
35 | public constructor ( | |
36 | public readonly min: number, | |
37 | public readonly max: number, | |
38 | public readonly filename: string, | |
39 | public readonly opts: DynamicThreadPoolOptions = { maxTasks: 1000 } | |
40 | ) { | |
41 | super(min, filename, opts) | |
42 | ||
43 | this.emitter = new MyEmitter() | |
44 | } | |
45 | ||
fa0f5b28 | 46 | protected chooseWorker (): WorkerWithMessageChannel { |
ee99693b | 47 | let worker: WorkerWithMessageChannel | undefined |
4ade5f1f S |
48 | for (const entry of this.tasks) { |
49 | if (entry[1] === 0) { | |
50 | worker = entry[0] | |
51 | break | |
52 | } | |
53 | } | |
54 | ||
55 | if (worker) { | |
56 | // a worker is free, use it | |
57 | return worker | |
58 | } else { | |
59 | if (this.workers.length === this.max) { | |
60 | this.emitter.emit('FullPool') | |
fa0f5b28 | 61 | return super.chooseWorker() |
4ade5f1f S |
62 | } |
63 | // all workers are busy create a new worker | |
fa0f5b28 | 64 | const worker = this.newWorker() |
ee99693b | 65 | worker.port2?.on('message', (message: { kill?: number }) => { |
4ade5f1f S |
66 | if (message.kill) { |
67 | worker.postMessage({ kill: 1 }) | |
ee99693b | 68 | void worker.terminate() |
68b2f517 S |
69 | // clean workers from data structures |
70 | const workerIndex = this.workers.indexOf(worker) | |
71 | this.workers.splice(workerIndex, 1) | |
72 | this.tasks.delete(worker) | |
4ade5f1f S |
73 | } |
74 | }) | |
75 | return worker | |
76 | } | |
77 | } | |
78 | } |