Skip to content

Commit

Permalink
Prevent infinite recursion where computed A updates B, which updates …
Browse files Browse the repository at this point in the history
…A, etc. by limiting the maximum number of task blocks in the same execution cycle to 5000.
  • Loading branch information
mbest committed Dec 4, 2013
1 parent 2c4588d commit ad5a8bf
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
20 changes: 16 additions & 4 deletions knockout-deferred-updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,26 @@ ko.tasks = (function() {
}

function processTasks(start) {
var countProcessed = 0;
var countProcessed = 0, countMarks = 0;

// Add a mark to the end of the queue; each one marks the end of a logical group of tasks
// and the number of these groups is limited to prevent unchecked recursion.
taskQueueEnd = taskQueueEnd._next = { _mark: true };

try {
for (var item = start; item = item._next; ) {
processingItem = item;
if (!item._done) {
if (item._mark) {
// When we encounter a mark, increment the mark counter and append a new mark to the queue
if (item._next) {
if (++countMarks >= 5000)
throw Error("'Too much recursion' after processing " + countProcessed + " tasks.");
taskQueueEnd = taskQueueEnd._next = { _mark: true };
}
} else if (!item._done) {
item._done = true;
item._func.apply(item.object, item.args || []);
countProcessed++;
++countProcessed;
}
}
} finally {
Expand All @@ -93,7 +105,7 @@ ko.tasks = (function() {
}
processingItem = undefined;
}
return countProcessed++;
return countProcessed;
}

function processAllTasks() {
Expand Down
12 changes: 12 additions & 0 deletions spec/deferredUpdatesBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,16 @@ describe('Deferred Updates', function() {
});
});

describe('Recursive updates', function() {
beforeEach(jasmine.prepareTestNode);

it('Should be prevented for value binding on multiple select boxes', function() {
testNode.innerHTML = "<select data-bind=\"options: ['abc','def','ghi'], value: x\"></select><select data-bind=\"options: ['xyz','uvw'], value: x\"></select>";
var observable = ko.observable();
expect(ko.tasks.makeProcessedCallback(function() {
ko.applyBindings({ x: observable }, testNode);
})).toThrowContaining('Too much recursion');
});
});

});

0 comments on commit ad5a8bf

Please sign in to comment.