An elegant way to prevent infinite loops and stack overflows

Preface

I primarily work with the Unity game engine, where creating an infinite loop results in the software being stuck, consuming all memory until it finally crashes or until I manually end the process in the task manager of the OS.


Even though it doesn’t happen that often, it’s quite a productivity killer as well as potential data loss when there are unsaved changes in the current scene.

Solution

To prevent this from ever happening again, I devised a simple solution, which I incorporate in every while loop to never have to deal with this nuisance.


The idea is as simple as it gets: Create a local counter variable that gets incremented at the start of every repetition and throws an error or leaves the loop when reaching a certain threshold.

int rep = 0;
while(**condition**) {
    if(++rep > 1000) throw new Exception("Infinite loop detected!");
    //**logic without break, return, etc.**
}

In languages like C# you can even create your own Exception to give this type of problem a more descriptive name for the debug console when encountering it, like so:

using System;

///<summary>Break after too many iterations to prevent an infinite loop.</summary>
public class InfiniteLoopException : Exception
{
    public InfiniteLoopException(string msg = "")
    {
        if (!string.IsNullOrEmpty(msg)) Log.Error(msg);
    }
}

What about other potential causes for crashes?

Similarly, stack overflows can occur when the call stack fills faster than calls are allocated to the stack, resulting in a memory leak and a similar crash or halting behavior, which can happen when implementing recursive logic where the control flow is uninterrupted and the logic chain continues downward unhindered.


In this case, a local counter variable can be added outside the recursive function call(s) and incremented at the start of the looping function.

private int depth = 0;

private void InA() => InB();

private void InB() {
    if (++depth > 1000) throw new Exception("Stack overflow detected!");
    InA();
}

Postface

When you are working on a product that allows users to create their own code logic, be it in the form of an IDE, a node/FSM graph or anything that can contain while loops or recursion, it’s best to add these checks natively with a configurable threshold that has a sensible default value as well as a notice in the warning/error message, that the loop exceeded the defined max iterations count and where to in the settings the user can adjust this restriction.


As an example: For the Unity game environment, there is an asset called PlayMaker that allows users to chain their code together in nodes instead of writing it in script form. If you create a recursive chain here by, for example, targeting a node at itself, when you run through it after 1000 iterations, the program will stop, with a notice that the limit of 1000 calls has been exceeded. No crash of the whole engine, no memory leak, just a friendly hint what the fault is.


This allows for a safer and better user experience, because no one wants the program to suddenly crash or stay unresponsive until all memory is consumed. Infinite loops can also have more detrimental effects, e.g. when sending call requests to a server on every iteration.


So the slight downside of adding a bit of extra logic to each while loop and recursive call is well worth it as opposed to the risk of shipping a product with potential crashes and difficult to reproduce bug reports.

October 2, 2023
Share