Commit | Line | Data |
---|---|---|
d3c8a1a8 | 1 | import type { JSONValue, MessageValue } from '../../utility-types' |
c97c7edb S |
2 | import type { PoolOptions } from '../abstract-pool' |
3 | import type { ThreadWorkerWithMessageChannel } from './fixed' | |
325f50bc | 4 | import { FixedThreadPool } from './fixed' |
f045358d | 5 | |
4ade5f1f | 6 | /** |
729c563d | 7 | * A thread pool with a dynamic number of threads, but a guaranteed minimum number of threads. |
4ade5f1f | 8 | * |
729c563d S |
9 | * This thread pool creates new threads when the others are busy, up to the maximum number of threads. |
10 | * When the maximum number of threads is reached, an event is emitted. If you want to listen to this event, use the pool's `emitter`. | |
11 | * | |
12 | * @template Data Type of data sent to the worker. | |
13 | * @template Response Type of response of execution. | |
4ade5f1f S |
14 | * |
15 | * @author [Alessandro Pio Ardizio](https://github.com/pioardi) | |
16 | * @since 0.0.1 | |
17 | */ | |
60fbd6d6 | 18 | export class DynamicThreadPool< |
d3c8a1a8 S |
19 | Data extends JSONValue = JSONValue, |
20 | Response extends JSONValue = JSONValue | |
4ade5f1f | 21 | > extends FixedThreadPool<Data, Response> { |
4ade5f1f | 22 | /** |
729c563d S |
23 | * Constructs a new poolifier dynamic thread pool. |
24 | * | |
25 | * @param min Minimum number of threads which are always active. | |
26 | * @param max Maximum number of threads that can be created by this pool. | |
27 | * @param filename Path to an implementation of a `ThreadWorker` file, which can be relative or absolute. | |
28 | * @param opts Options for this fixed thread pool. Default: `{ maxTasks: 1000 }` | |
4ade5f1f S |
29 | */ |
30 | public constructor ( | |
c97c7edb | 31 | min: number, |
4ade5f1f | 32 | public readonly max: number, |
c97c7edb S |
33 | filename: string, |
34 | opts: PoolOptions<ThreadWorkerWithMessageChannel> = { maxTasks: 1000 } | |
4ade5f1f S |
35 | ) { |
36 | super(min, filename, opts) | |
4ade5f1f S |
37 | } |
38 | ||
729c563d S |
39 | /** |
40 | * Choose a thread for the next task. | |
41 | * | |
42 | * It will first check for and return an idle thread. | |
43 | * If all threads are busy, then it will try to create a new one up to the `max` thread count. | |
44 | * If the max thread count is reached, the emitter will emit a `FullPool` event and it will fall back to using a round robin algorithm to distribute the load. | |
50eceb07 S |
45 | * |
46 | * @returns Thread worker. | |
729c563d | 47 | */ |
c97c7edb S |
48 | protected chooseWorker (): ThreadWorkerWithMessageChannel { |
49 | let worker: ThreadWorkerWithMessageChannel | undefined | |
4ade5f1f S |
50 | for (const entry of this.tasks) { |
51 | if (entry[1] === 0) { | |
52 | worker = entry[0] | |
53 | break | |
54 | } | |
55 | } | |
56 | ||
57 | if (worker) { | |
280c2a77 | 58 | // A worker is free, use it |
4ade5f1f S |
59 | return worker |
60 | } else { | |
61 | if (this.workers.length === this.max) { | |
62 | this.emitter.emit('FullPool') | |
fa0f5b28 | 63 | return super.chooseWorker() |
4ade5f1f | 64 | } |
280c2a77 S |
65 | // All workers are busy, create a new worker |
66 | const worker = this.createAndSetupWorker() | |
c97c7edb | 67 | worker.port2?.on('message', (message: MessageValue<Data>) => { |
4ade5f1f | 68 | if (message.kill) { |
c97c7edb S |
69 | this.sendToWorker(worker, { kill: 1 }) |
70 | void this.destroyWorker(worker) | |
f2fdaa86 | 71 | this.removeWorker(worker) |
4ade5f1f S |
72 | } |
73 | }) | |
74 | return worker | |
75 | } | |
76 | } | |
77 | } |