Rename functions and methods to not use prefix underscore (#86)
[poolifier.git] / src / dynamic.ts
CommitLineData
ee99693b
S
1/* eslint-disable @typescript-eslint/strict-boolean-expressions */
2
60fbd6d6
S
3import {
4 FixedThreadPool,
f045358d
S
5 FixedThreadPoolOptions,
6 WorkerWithMessageChannel
7} from './fixed'
8
4ade5f1f 9import { EventEmitter } from 'events'
4ade5f1f
S
10
11class MyEmitter extends EventEmitter {}
12
13export 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 24export 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}