Fix massive timeout/interval memory leaks

This commit is contained in:
Schuyler Cebulskie
2016-09-19 03:49:42 -04:00
parent 6f7deba4b3
commit 7d8667694d
5 changed files with 41 additions and 28 deletions

View File

@@ -103,8 +103,8 @@ class Client extends EventEmitter {
* @type {?Date}
*/
this.readyTime = null;
this._intervals = [];
this._timeouts = [];
this._intervals = [];
}
/**
@@ -138,34 +138,18 @@ class Client extends EventEmitter {
destroy() {
return new Promise((resolve, reject) => {
this.manager.destroy().then(() => {
this._intervals.map(i => clearInterval(i));
this._timeouts.map(t => clearTimeout(t));
for (const i of this._intervals) clearInterval(i);
for (const t of this._timeouts) clearTimeout(t);
this._timeouts = [];
this._intervals = [];
this.token = null;
this.email = null;
this.password = null;
this._timeouts = [];
this._intervals = [];
resolve();
}).catch(reject);
});
}
setInterval(...params) {
const interval = setInterval(...params);
this._intervals.push(interval);
return interval;
}
setTimeout(...params) {
const restParams = params.slice(1);
const timeout = setTimeout(() => {
this._timeouts.splice(this._timeouts.indexOf(params[0]), 1);
params[0]();
}, ...restParams);
this._timeouts.push(timeout);
return timeout;
}
/**
* This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
* if you wish to force a sync of Guild data, you can use this. Only applicable to user accounts.
@@ -237,6 +221,31 @@ class Client extends EventEmitter {
get status() {
return this.ws.status;
}
setTimeout(fn, ...params) {
const timeout = setTimeout(() => {
fn();
this._timeouts.splice(this._timeouts.indexOf(timeout), 1);
}, ...params);
this._timeouts.push(timeout);
return timeout;
}
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.splice(this._timeouts.indexOf(timeout), 1);
}
setInterval(...params) {
const interval = setInterval(...params);
this._intervals.push(interval);
return interval;
}
clearInterval(interval) {
clearInterval(interval);
this._intervals.splice(this._intervals.indexOf(interval), 1);
}
}
module.exports = Client;

View File

@@ -31,12 +31,15 @@ class ClientManager {
*/
this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
this.client.token = token;
const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
this.client.rest.methods.getGateway().then(gateway => {
this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
this.client.ws.connect(gateway);
this.client.once(Constants.Events.READY, () => resolve(token));
this.client.once(Constants.Events.READY, () => {
resolve(token);
this.client.clearTimeout(timeout);
});
}).catch(reject);
this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
}
/**

View File

@@ -31,7 +31,7 @@ class VoiceConnectionWebSocket extends EventEmitter {
_shutdown() {
if (this.ws) this.ws.close();
clearInterval(this.heartbeat);
this.voiceConnection.manager.client.clearInterval(this.heartbeat);
}
_onOpen() {

View File

@@ -19,7 +19,7 @@ class TypingStartHandler extends AbstractHandler {
typing.lastTimestamp = timestamp;
typing.resetTimeout(tooLate(channel, user));
} else {
channel._typing.set(user.id, new TypingData(timestamp, timestamp, tooLate(channel, user)));
channel._typing.set(user.id, new TypingData(client, timestamp, timestamp, tooLate(channel, user)));
client.emit(Constants.Events.TYPING_START, channel, user);
}
}
@@ -27,14 +27,15 @@ class TypingStartHandler extends AbstractHandler {
}
class TypingData {
constructor(since, lastTimestamp, _timeout) {
constructor(client, since, lastTimestamp, _timeout) {
this.client = client;
this.since = since;
this.lastTimestamp = lastTimestamp;
this._timeout = _timeout;
}
resetTimeout(_timeout) {
clearTimeout(this._timeout);
this.client.clearTimeout(this._timeout);
this._timeout = _timeout;
}

View File

@@ -232,7 +232,7 @@ class TextBasedChannel {
const entry = this.client.user._typing.get(this.id);
entry.count--;
if (entry.count <= 0 || force) {
clearInterval(entry.interval);
this.client.clearInterval(entry.interval);
this.client.user._typing.delete(this.id);
}
}