Skip to content

Commit

Permalink
fix(asapScheduler): resolved memory leak (#5183)
Browse files Browse the repository at this point in the history
Registered handlers would sometimes leak in memory, this resolves that issue and adds a test.

Related #5016
  • Loading branch information
benlesh committed Dec 16, 2019
1 parent 2c2f68c commit 4312af5
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 10 deletions.
14 changes: 13 additions & 1 deletion spec/util/Immediate-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { Immediate } from 'rxjs/util/Immediate';
import { Immediate, TestTools } from 'rxjs/internal/util/Immediate';

describe('Immediate', () => {
it('should schedule on the next microtask', (done) => {
Expand Down Expand Up @@ -30,4 +30,16 @@ describe('Immediate', () => {
done();
});
});

it('should clear the task after execution', (done) => {
const results: number[] = [];
Immediate.setImmediate(() => results.push(1));
Immediate.setImmediate(() => results.push(2));

setTimeout(() => {
const number = TestTools.pending();
expect(number).to.equal(0);
done();
});
});
});
36 changes: 27 additions & 9 deletions src/internal/util/Immediate.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
let nextHandle = 1;
const RESOLVED = (() => Promise.resolve())();
const activeHandles: { [key: number]: any } = {};

const tasksByHandle: { [handle: string]: () => void } = {};

function runIfPresent(handle: number) {
const cb = tasksByHandle[handle];
if (cb) {
cb();
/**
* Finds the handle in the list of active handles, and removes it.
* Returns `true` if found, `false` otherwise. Used both to clear
* Immediate scheduled tasks, and to identify if a task should be scheduled.
*/
function findAndClearHandle(handle: number): boolean {
if (handle in activeHandles) {
delete activeHandles[handle];
return true;
}
return false;
}

/**
* Helper functions to schedule and unschedule microtasks.
*/
export const Immediate = {
setImmediate(cb: () => void): number {
const handle = nextHandle++;
tasksByHandle[handle] = cb;
Promise.resolve().then(() => runIfPresent(handle));
activeHandles[handle] = true;
RESOLVED.then(() => findAndClearHandle(handle) && cb());
return handle;
},

clearImmediate(handle: number): void {
delete tasksByHandle[handle];
findAndClearHandle(handle);
},
};

/**
* Used for internal testing purposes only. Do not export from library.
*/
export const TestTools = {
pending() {
return Object.keys(activeHandles).length;
}
};

0 comments on commit 4312af5

Please sign in to comment.