From 18809b39020778601090b8d268e3590a39512176 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 22 Sep 2020 13:45:57 -0700 Subject: [PATCH] fix(listeners): avoid "too many listeners" problem (#3931) We currently spawn a process per page when recording video in Chromium. This triggers "too many listeners" on the process object once you have enough pages open. --- src/server/processLauncher.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/server/processLauncher.ts b/src/server/processLauncher.ts index b19281a7a0..3a4d0df206 100644 --- a/src/server/processLauncher.ts +++ b/src/server/processLauncher.ts @@ -23,6 +23,7 @@ import { helper } from './helper'; import { Progress } from './progress'; import * as types from './types'; import { isUnderTest } from '../utils/utils'; +import { EventEmitter } from 'events'; export type Env = {[key: string]: string | number | boolean | undefined}; @@ -58,6 +59,26 @@ export async function gracefullyCloseAll() { await Promise.all(Array.from(gracefullyCloseSet).map(gracefullyClose => gracefullyClose().catch(e => {}))); } +class EventEmitterWrapper extends EventEmitter { + private _wrappedEvents: Set; + constructor(emitter: EventEmitter) { + super(); + this.setMaxListeners(0); + this._wrappedEvents = new Set(); + for (const method of ['addListener', 'on', 'once', 'prependListener', 'prependOnceListener'] as const) { + this[method] = (event: string | symbol, listener: (...args: any[]) => void) => { + if (!this._wrappedEvents.has(event)) { + this._wrappedEvents.add(event); + emitter.addListener(event, (...eventArgs) => this.emit(event, ...eventArgs)); + } + return super[method](event, listener); + }; + } + } +} + +const processWrapper = new EventEmitterWrapper(process); + export async function launchProcess(options: LaunchProcessOptions): Promise { const cleanup = () => helper.removeFolders(options.tempDirectories); @@ -115,9 +136,9 @@ export async function launchProcess(options: LaunchProcessOptions): Promise { + listeners.push(helper.addEventListener(processWrapper, 'SIGINT', () => { gracefullyClose().then(() => { // Give tests a chance to dispatch any async calls. if (isUnderTest()) @@ -128,9 +149,9 @@ export async function launchProcess(options: LaunchProcessOptions): Promise