How to handle infinite loops within user code

Infinite loops are a ache. Usually, working an infinite loop can eat up your pc’s assets after which freeze your IDE—and typically even your complete machine. Contained in the CodeSignal IDE, letting infinite loops run unchecked would finally decelerate or crash the UI. These UI points would make issues harder for our customers and have an effect on the standard of assessments, so we would have liked to make sure we offer a responsive and helpful programming atmosphere. 

To that finish, we needed to discover a method to gracefully deal with the issue of customers writing infinite loops into their code. Even when the halting problem proves that we may by no means completely detect infinite loops, by specializing in fixing this downside for our particular product and infrastructure, we discovered options that defend our customers’ experiences.

Dealing with Infinite Loops

Primary Answer: Code Instrumentation

Code instrumentation is a typical resolution for dealing with infinite loops. Mainly, an instrumentation device can monitor and break infinite loops. We maintained the loop-protect package deal in our codebase, which rewrites JavaScript code to interrupt loops which were working too lengthy. Whereas a extra difficult evaluation could have enabled us to detect (some) situations of infinite looping analytically, for our CodeSignal IDE, we may go along with an easier resolution and place an affordable timeout on how lengthy a loop ought to have the ability to run. We keep away from the halting downside as a result of we don’t must resolve if any piece of code will finally terminate—if it might take a 12 months for a candidate’s code to complete working, it’s most likely not helpful for his or her evaluation.

Nevertheless, this code instrumentation device may solely deal with infinite loops precipitated explicitly by the `whereas`, `for`, and `do…whereas` key phrases. Sadly, infinite loops may also be launched implicitly. For instance, a frontend React job may introduce an infinite loop by utilizing and updating the identical state collectively within the `useEffect` hook: 

 const [count, setCount] = useState(-1);
 useEffect(() => setCount(depend + 1), [count]);

Right here, updating depend will name `useEffect`, which is able to replace `depend`, which is able to name `useEffect`, and so forth endlessly

This sort of infinite loop wouldn’t be caught by the `loop-protect` code instrumentation.

Personalized Answer: Checking In with Pings

I appeared for the standard resolution or package deal to deal with these sorts of infinite loops that stemmed from the improper use of frontend frameworks, however I discovered nothing in my search. Since there was no common resolution, I as a substitute began eager about some personalized approaches based mostly on CodeSignal’s infrastructure.

Happily, I discovered a means that our infrastructure would enable us to deal with such a infinite loop. First, some context: In our frontend, the UI is rendered utilizing an iframe, and the iframe is hosted on a special server, with a special area, over which we now have full management. These days, most trendy browsers will run an iframe from a special area in a separate thread from the principle app. When there’s an infinite loop, then, solely the assets within the iframe thread can be eaten up, whereas different threads can run as regular. 

Based mostly on this, I carried out a easy ping mechanism: The iframe thread usually pings the thread that runs the React app, and if the React app doesn’t obtain the ping inside a sure timeframe, it stops rendering the iframe. 

So, when an infinite loop occurs, increasingly more assets within the iframe thread get consumed. This causes longer and longer delays when triggering the subsequent interval, and thus, the ping to the React thread is delayed. On the React thread, it checks whether or not it receives a ping from the iframe thread on a daily cadence. If it doesn’t obtain the ping earlier than it occasions out, we assume there’s an infinite loop ongoing and halt the iframe’s rendering. With this mechanism in place, the consumer’s browser tab doesn’t freeze because of the infinite loop. 

It’s necessary to search out the suitable stability right here. The timeout must be lengthy sufficient that affordable options will have the ability to run even when they’re sluggish and trigger a minor slowdown within the iframe thread. If we let infinite loops run for too lengthy, although, it’s potential that the entire browser will freeze earlier than the timeout is triggered. 

Right here’s a pseudocode instance of find out how to arrange this ping: 

iframeScript.js

setInterval(() => 
   // ship a ping occasion to the React thread each 2 sec
   mum or dad.postMessage(
     
       motion: 'iframe-ping',
     ,
     '*'
   );
 , 2000);

React part

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.information.motion === ‘iframe-ping’) 
    this.isIframePing = true;
  
 

Ultimately, this bespoke resolution got here from our understanding of what our product necessities are and what our infrastructure may help. The halting downside is fascinating and necessary, however for our wants we didn’t have to resolve the entire thing (fortunate for us, as a result of that’s actually inconceivable).

For those who like to construct nice merchandise and discover options to thorny issues, we’re hiring. Be a part of us, and hopefully sometime we’ll have the ability to showcase your personal ingenuity proper right here on this weblog!  


Zhoucheng Li is a Software program Engineer at CodeSignal. In his work, he focuses on designing and constructing options in CodeSignal’s merchandise utilizing completely different tech stacks.

More Posts