+ const workerNodeKey = this.addWorkerNode(worker)
+
+ this.afterWorkerNodeSetup(workerNodeKey)
+
+ return workerNodeKey
+ }
+
+ /**
+ * Creates a new, completely set up dynamic worker node.
+ *
+ * @returns New, completely set up dynamic worker node key.
+ */
+ protected createAndSetupDynamicWorkerNode (): number {
+ const workerNodeKey = this.createAndSetupWorkerNode()
+ this.registerWorkerMessageListener(workerNodeKey, message => {
+ const localWorkerNodeKey = this.getWorkerNodeKeyByWorkerId(
+ message.workerId
+ )
+ const workerUsage = this.workerNodes[localWorkerNodeKey].usage
+ // Kill message received from worker
+ if (
+ isKillBehavior(KillBehaviors.HARD, message.kill) ||
+ (isKillBehavior(KillBehaviors.SOFT, message.kill) &&
+ ((this.opts.enableTasksQueue === false &&
+ workerUsage.tasks.executing === 0) ||
+ (this.opts.enableTasksQueue === true &&
+ workerUsage.tasks.executing === 0 &&
+ this.tasksQueueSize(localWorkerNodeKey) === 0)))
+ ) {
+ this.destroyWorkerNode(localWorkerNodeKey).catch(error => {
+ this.emitter?.emit(PoolEvents.error, error)
+ })
+ }
+ })
+ const workerInfo = this.getWorkerInfo(workerNodeKey)
+ this.sendToWorker(workerNodeKey, {
+ checkActive: true,
+ workerId: workerInfo.id as number
+ })
+ workerInfo.dynamic = true
+ if (
+ this.workerChoiceStrategyContext.getStrategyPolicy().dynamicWorkerReady ||
+ this.workerChoiceStrategyContext.getStrategyPolicy().dynamicWorkerUsage
+ ) {
+ workerInfo.ready = true
+ }
+ this.checkAndEmitDynamicWorkerCreationEvents()
+ return workerNodeKey
+ }
+
+ /**
+ * Registers a listener callback on the worker given its worker node key.
+ *
+ * @param workerNodeKey - The worker node key.
+ * @param listener - The message listener callback.
+ */
+ protected abstract registerWorkerMessageListener<
+ Message extends Data | Response
+ >(
+ workerNodeKey: number,
+ listener: (message: MessageValue<Message>) => void
+ ): void
+
+ /**
+ * Method hooked up after a worker node has been newly created.
+ * Can be overridden.
+ *
+ * @param workerNodeKey - The newly created worker node key.
+ */
+ protected afterWorkerNodeSetup (workerNodeKey: number): void {
+ // Listen to worker messages.
+ this.registerWorkerMessageListener(workerNodeKey, this.workerListener())
+ // Send the startup message to worker.
+ this.sendStartupMessageToWorker(workerNodeKey)
+ // Send the statistics message to worker.
+ this.sendStatisticsMessageToWorker(workerNodeKey)
+ if (this.opts.enableTasksQueue === true) {
+ this.workerNodes[workerNodeKey].onEmptyQueue =
+ this.taskStealingOnEmptyQueue.bind(this)
+ this.workerNodes[workerNodeKey].onBackPressure =
+ this.tasksStealingOnBackPressure.bind(this)
+ }
+ }
+
+ /**
+ * Sends the startup message to worker given its worker node key.
+ *
+ * @param workerNodeKey - The worker node key.
+ */
+ protected abstract sendStartupMessageToWorker (workerNodeKey: number): void
+
+ /**
+ * Sends the statistics message to worker given its worker node key.
+ *
+ * @param workerNodeKey - The worker node key.
+ */
+ private sendStatisticsMessageToWorker (workerNodeKey: number): void {
+ this.sendToWorker(workerNodeKey, {
+ statistics: {
+ runTime:
+ this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ .runTime.aggregate,
+ elu: this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ .elu.aggregate
+ },
+ workerId: this.getWorkerInfo(workerNodeKey).id as number
+ })
+ }
+
+ private redistributeQueuedTasks (workerNodeKey: number): void {
+ while (this.tasksQueueSize(workerNodeKey) > 0) {
+ const destinationWorkerNodeKey = this.workerNodes.reduce(
+ (minWorkerNodeKey, workerNode, workerNodeKey, workerNodes) => {
+ return workerNode.info.ready &&
+ workerNode.usage.tasks.queued <
+ workerNodes[minWorkerNodeKey].usage.tasks.queued
+ ? workerNodeKey
+ : minWorkerNodeKey
+ },
+ 0
+ )
+ const destinationWorkerNode = this.workerNodes[destinationWorkerNodeKey]
+ const task = {
+ ...(this.dequeueTask(workerNodeKey) as Task<Data>),
+ workerId: destinationWorkerNode.info.id as number
+ }
+ if (this.shallExecuteTask(destinationWorkerNodeKey)) {
+ this.executeTask(destinationWorkerNodeKey, task)
+ } else {
+ this.enqueueTask(destinationWorkerNodeKey, task)
+ }
+ }
+ }