Detecting infinite loops in any arbritrary piece of JavaScript is difficult.

Difficult because when you need to stop any piece of JavaScript from executing forever you must be sure you've discovered an infinite loop, otherwise you're going to make writing JavaScript infinitely frustrating.

This was the problem we faced at CodePen. I originally wrote about how I was attempting to stop infinite loops in Pens in this article.

Two Solutions Two More Problems

Using the excellent Esprima library we were taking JavaScript like this:

  for (var i = 0;; i++) {
  console.log("hello for loop.");
}

console.log("We'll never reach this line of code");

And converting it to this:

  for (var i = 0;; i++) {
    if (window.CP.shouldStopExecution(1)) {
        break;
    }
    console.log('hello for loop.');
}

console.log("We'll never reach this line of code");

The logic seemed simple.

Add a call to a CodePen function prior to executing a loop code block. This gives CodePen a chance to detect and kill infinite loops.

The number passed to window.CP.shouldStopExecution is actually a loop ID. Every loop gets one.

Failed Solution #1

The first solution used the loop ID to determine how long a single loop ran. If it took more than 75 milliseconds we called break and exited the loop. This seemed sensical and simple until we found Pens that successfully ran for much longer.

One of the most frustrating parts of detecting infinite loops is measuring the loop's execution time. It requires a call to new Date().getTime() that is very CPU intensive.

The constant calls to shouldStopExecution choked the JavaScript program. It was detecting a problem it was causing!

This didn't stop this stoic programmer.

Failed Solution #2

Next I tried to calculate the frames per second for the first 2 seconds of the program. If the program ever dipped below a frames per second threshold for X amount of milliseconds shouldStopExecution called break. This caused a ton of false positives.

Getting an accurate reading of the number of frames per second is at best an estimation with JavaScript. It is nearly impossible to differentiate between stuck and normal high CPU usage when calculating frames per second. We scrapped this too.

Ever the stoic, I carried on.

I'll take Solution #3 for $800 Alex

How can you tell if a program can exit a loop?

If it exits a loop, the only way you know a program can exit a loop is. Yes, hmmm.

- Master Yoda

You're goddamn right Yoda. This was my eureka moment.

I'd gladly detect 85% of the infinite loops if I could have zero false positives.

  for (var i = 0;; i++) {
    if (window.CP.shouldStopExecution(1)) {
        break;
    }
    console.log('hello for loop.');
}

window.CP.exitedLoop(1); 
// our new logic should allow us to get here
console.log("We'll never reach this line of code");

See what we did there?

We added a matching function call to shouldStopExecution(1) called exitedLoop(1). CodePen keeps track of every entrance and exit into a while, do or for loop. If a loop ever exits we don't consider it an infinite loop.

We're looking to stop the most common infinite loops (function recursion not included). If a loop runs for more than 2200 milliseconds without exiting we consider it an infinite loop and break.

This simple logic allowed a pen that looped 205,000 times to execute yet it still caught the infinite loop I created above.

+1 for stupidly simple engineering.

The CodePen editor even highlights the line number where it thinks the infinte loop exist.

This probably isn't the last time I'll revisit this code but I'm confident the solution will fit 99% of CodePen's use cases.


7,735 14 31