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