Commit | Line | Data |
---|---|---|
4f7fa42a | 1 | import type { JSONValue } from '../../utility-types' |
1a81f8af | 2 | import { isKillBehavior, KillBehaviors } from '../../worker/worker-options' |
c97c7edb S |
3 | import type { PoolOptions } from '../abstract-pool' |
4 | import type { ThreadWorkerWithMessageChannel } from './fixed' | |
325f50bc | 5 | import { FixedThreadPool } from './fixed' |
f045358d | 6 | |
4ade5f1f | 7 | /** |
729c563d | 8 | * A thread pool with a dynamic number of threads, but a guaranteed minimum number of threads. |
4ade5f1f | 9 | * |
729c563d S |
10 | * This thread pool creates new threads when the others are busy, up to the maximum number of threads. |
11 | * 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`. | |
12 | * | |
13 | * @template Data Type of data sent to the worker. | |
14 | * @template Response Type of response of execution. | |
4ade5f1f S |
15 | * |
16 | * @author [Alessandro Pio Ardizio](https://github.com/pioardi) | |
17 | * @since 0.0.1 | |
18 | */ | |
60fbd6d6 | 19 | export class DynamicThreadPool< |
d3c8a1a8 S |
20 | Data extends JSONValue = JSONValue, |
21 | Response extends JSONValue = JSONValue | |
4ade5f1f | 22 | > extends FixedThreadPool<Data, Response> { |
4ade5f1f | 23 | /** |
729c563d S |
24 | * Constructs a new poolifier dynamic thread pool. |
25 | * | |
26 | * @param min Minimum number of threads which are always active. | |
27 | * @param max Maximum number of threads that can be created by this pool. | |
31b90205 JB |
28 | * @param filePath Path to an implementation of a `ThreadWorker` file, which can be relative or absolute. |
29 | * @param opts Options for this dynamic thread pool. Default: `{ maxTasks: 1000 }` | |
4ade5f1f S |
30 | */ |
31 | public constructor ( | |
c97c7edb | 32 | min: number, |
4ade5f1f | 33 | public readonly max: number, |
31b90205 | 34 | filePath: string, |
c97c7edb | 35 | opts: PoolOptions<ThreadWorkerWithMessageChannel> = { maxTasks: 1000 } |
4ade5f1f | 36 | ) { |
31b90205 | 37 | super(min, filePath, opts) |
4ade5f1f S |
38 | } |
39 | ||
729c563d S |
40 | /** |
41 | * Choose a thread for the next task. | |
42 | * | |
43 | * It will first check for and return an idle thread. | |
44 | * If all threads are busy, then it will try to create a new one up to the `max` thread count. | |
45 | * 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 |
46 | * |
47 | * @returns Thread worker. | |
729c563d | 48 | */ |
c97c7edb | 49 | protected chooseWorker (): ThreadWorkerWithMessageChannel { |
4f7fa42a S |
50 | for (const [worker, numberOfTasks] of this.tasks) { |
51 | if (numberOfTasks === 0) { | |
52 | // A worker is free, use it | |
53 | return worker | |
4ade5f1f S |
54 | } |
55 | } | |
56 | ||
4f7fa42a S |
57 | if (this.workers.length === this.max) { |
58 | this.emitter.emit('FullPool') | |
59 | return super.chooseWorker() | |
4ade5f1f | 60 | } |
4f7fa42a S |
61 | |
62 | // All workers are busy, create a new worker | |
3ec964d6 | 63 | const workerCreated = this.createAndSetupWorker() |
64 | this.registerWorkerMessageListener<Data>(workerCreated, message => { | |
65 | const tasksInProgress = this.tasks.get(workerCreated) | |
1a81f8af S |
66 | if ( |
67 | isKillBehavior(KillBehaviors.HARD, message.kill) || | |
68 | tasksInProgress === 0 | |
69 | ) { | |
d63d3be3 | 70 | // Kill received from the worker, means that no new tasks are submitted to that worker for a while ( > maxInactiveTime) |
3ec964d6 | 71 | void this.destroyWorker(workerCreated) |
4f7fa42a S |
72 | } |
73 | }) | |
3ec964d6 | 74 | return workerCreated |
4ade5f1f S |
75 | } |
76 | } |