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.
This commit is contained in:
parent
2d1cabdd3f
commit
18809b3902
|
|
@ -23,6 +23,7 @@ import { helper } from './helper';
|
||||||
import { Progress } from './progress';
|
import { Progress } from './progress';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { isUnderTest } from '../utils/utils';
|
import { isUnderTest } from '../utils/utils';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
export type Env = {[key: string]: string | number | boolean | undefined};
|
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 => {})));
|
await Promise.all(Array.from(gracefullyCloseSet).map(gracefullyClose => gracefullyClose().catch(e => {})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EventEmitterWrapper extends EventEmitter {
|
||||||
|
private _wrappedEvents: Set<string | symbol>;
|
||||||
|
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<LaunchResult> {
|
export async function launchProcess(options: LaunchProcessOptions): Promise<LaunchResult> {
|
||||||
const cleanup = () => helper.removeFolders(options.tempDirectories);
|
const cleanup = () => helper.removeFolders(options.tempDirectories);
|
||||||
|
|
||||||
|
|
@ -115,9 +136,9 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
cleanup().then(fulfillCleanup);
|
cleanup().then(fulfillCleanup);
|
||||||
});
|
});
|
||||||
|
|
||||||
const listeners = [ helper.addEventListener(process, 'exit', killProcess) ];
|
const listeners = [ helper.addEventListener(processWrapper, 'exit', killProcess) ];
|
||||||
if (options.handleSIGINT) {
|
if (options.handleSIGINT) {
|
||||||
listeners.push(helper.addEventListener(process, 'SIGINT', () => {
|
listeners.push(helper.addEventListener(processWrapper, 'SIGINT', () => {
|
||||||
gracefullyClose().then(() => {
|
gracefullyClose().then(() => {
|
||||||
// Give tests a chance to dispatch any async calls.
|
// Give tests a chance to dispatch any async calls.
|
||||||
if (isUnderTest())
|
if (isUnderTest())
|
||||||
|
|
@ -128,9 +149,9 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (options.handleSIGTERM)
|
if (options.handleSIGTERM)
|
||||||
listeners.push(helper.addEventListener(process, 'SIGTERM', gracefullyClose));
|
listeners.push(helper.addEventListener(processWrapper, 'SIGTERM', gracefullyClose));
|
||||||
if (options.handleSIGHUP)
|
if (options.handleSIGHUP)
|
||||||
listeners.push(helper.addEventListener(process, 'SIGHUP', gracefullyClose));
|
listeners.push(helper.addEventListener(processWrapper, 'SIGHUP', gracefullyClose));
|
||||||
gracefullyCloseSet.add(gracefullyClose);
|
gracefullyCloseSet.add(gracefullyClose);
|
||||||
|
|
||||||
let gracefullyClosing = false;
|
let gracefullyClosing = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue