Infinite loops are a pain. In general, running an infinite loop can eat up your computer’s resources and then freeze your IDE—and sometimes even your entire machine. Inside the CodeSignal IDE, letting infinite loops run unchecked would eventually slow down or crash the UI. These UI issues would make things more difficult for our users and affect the quality of assessments, so we needed to ensure we provide a responsive and useful programming environment.
To that end, we had to find a way to gracefully handle the problem of users writing infinite loops into their code. Even if the halting problem proves that we could never perfectly detect infinite loops, by focusing on solving this problem for our specific product and infrastructure, we found solutions that protect our users’ experiences.
Handling Infinite Loops
Basic Solution: Code Instrumentation
However, this code instrumentation tool could only handle infinite loops caused explicitly by the `while`, `for`, and `do…while` keywords. Unfortunately, infinite loops can also be introduced implicitly. For example, a frontend React task could introduce an infinite loop by using and updating the same state together in the `useEffect` hook:
const [count, setCount] = useState(-1); useEffect(() => setCount(count + 1), [count]);
Here, updating count will call `useEffect`, which will update `count`, which will call `useEffect`, and so on forever
This type of infinite loop would not be caught by the `loop-protect’ code instrumentation.
Customized Solution: Checking In with Pings
I looked for a standard solution or package to handle these kinds of infinite loops that stemmed from the improper use of frontend frameworks, but I found nothing in my search. Since there was no general solution, I instead started thinking about some customized approaches based on CodeSignal’s infrastructure.
Fortunately, I found a way that our infrastructure would allow us to handle this type of infinite loop. First, some context: In our frontend, the UI is rendered using an iframe, and the iframe is hosted on a different server, with a different domain, over which we have full control. Nowadays, most modern browsers will run an iframe from a different domain in a separate thread from the main app. When there is an infinite loop, then, only the resources in the iframe thread will be eaten up, while other threads can run as normal.
Based on this, I implemented a simple ping mechanism: The iframe thread regularly pings the thread that runs the React app, and if the React app doesn’t receive the ping within a certain timeframe, it stops rendering the iframe.
So, when an infinite loop happens, more and more resources in the iframe thread get consumed. This causes longer and longer delays when triggering the next interval, and thus, the ping to the React thread is delayed. On the React thread, it checks whether it receives a ping from the iframe thread on a regular cadence. If it doesn’t receive the ping before it times out, we assume there’s an infinite loop going on and halt the iframe’s rendering. With this mechanism in place, the user’s browser tab doesn’t freeze due to the infinite loop.
It’s important to find the right balance here. The timeout needs to be long enough that reasonable solutions will be able to run even if they’re slow and cause a minor slowdown in the iframe thread. If we let infinite loops run for too long, though, it is possible that the entire browser will freeze before the timeout is triggered.
Here’s a pseudocode example of how to set up this ping:
setInterval(() => // send a ping event to the React thread every 2 sec parent.postMessage( action: 'iframe-ping', , '*' ); , 2000);
Class IdeComponent componentDidMount() setInterval(() => If (!this.isIframePing) // halt iframe iframe.src = ‘’; else this.iframePing = false; , 10000) window.addEventListener('message', this.processMessage, false); processMessage(message) if (message.data.action === ‘iframe-ping’) this.isIframePing = true;
In the end, this bespoke solution came from our understanding of what our product requirements are and what our infrastructure could support. The halting problem is fascinating and important, but for our needs we didn’t have to solve the whole thing (lucky for us, because that’s literally impossible).
If you love to build great products and find solutions to thorny problems, we’re hiring. Join us, and hopefully someday we’ll be able to show off your own ingenuity right here on this blog!
Zhoucheng Li is a Software Engineer at CodeSignal. In his work, he focuses on designing and building features in CodeSignal’s products using different tech stacks.