1

I am working on a big game in Unity, and one of my methods sometimes goes into an endless loop. For example this might be the code I'm using:

public void veryBigMethod(int n) {

    if (n % 2 == 0) {
        while (true) {
            // do a lot of work here
            // that might lead to an infinite loop
        }
    }
    else {
        // much shorter code that never fails
    }
}

I have tried this solution, but it doesn't work:

public void runLoopNeverStuck() {
    Thread t = new Thread(veryBigMethod);
    t.Start();
    if (!t.Join(TimeSpan.FromSeconds(30))) {
        t.Abort();
        throw new Exception("More than 30 secs.");
    }
}

What should I do using only code to detect such a loop and stop it?

Why doesn't my code above work on Unity?

Important note I can't use Task / async in my code, so please give me answers with threads

Draken
  • 3,134
  • 13
  • 34
  • 54
Tal Angel
  • 1,301
  • 3
  • 29
  • 63
  • Forget that `Thread` as the `Abort` Method ... – Fildor Jan 07 '20 at 08:34
  • Does this answer your question? [How to cancel a Task in await?](https://stackoverflow.com/questions/10134310/how-to-cancel-a-task-in-await) – Cid Jan 07 '20 at 08:37
  • What is missing here: _why_ does it end up infinite? And how do you want to handle it? Do you want to detect up front? Do you want to simply have a timeout? Do you want to compute an approximation, so allow N iterarions, for example? – Fildor Jan 07 '20 at 08:37
  • 4
    You can't detect infinite loops, because there is no way to determine if it actually runs infinitely, or just *very very long* (Halting problem). Something close you can do is add additional condition(s) that fail after `n` amount of iterations or time breaking the loop... However you'll still have to determine that limit yourself, and can't rely on something "detecting" if its stuck infinitely. Rather then looking at how to cure the infinite loop i'd look at preventing it, as infinite loops indicate a flaw in your code design – Remy Jan 07 '20 at 09:27
  • @Remy _"You can't detect infinite loops"_ - well, if it's deterministic, then you could _maybe_ predict infinite runtime from the start parameters. But that's many "if"s. – Fildor Jan 07 '20 at 09:34
  • 2
    If you have the barely remote suspect that you may have an infinite loop, it means you are unsure about the termination conditions of your code. Go find if you have a bug if yoususpect there is one. You don't need more code, you need to be confident in your actual code. – Andrea Rossini Jan 07 '20 at 13:28

2 Answers2

1

You can create a timer object and update identifier value on elasped.

bool stopLoop = false;

System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = TimeSpan.FromSeconds(30);
timer.Elapsed += (s, e) => stopLoop = true;
timer.Start();

Now check for stopLoop identifier in your loop and break when it is set to true

public void veryBigMethod(int n) {

    if (n % 2 == 0) {
        while (true) {
            // do a lot of work here
            // that might lead to infinite loop

            if (stopLoop) {
               break;
            }
        }
    }
    else {
        // much shorter code the never fails
    }
}
Muhammad Saqlain
  • 2,112
  • 4
  • 33
  • 48
1

You can use cancellation tokens to abort a Task after your desired timeout has been reached. For example:

bool timedOut;
CancellationToken ct;

private IEnumerator PerformTimedAction(Action action, int timeout = 5)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    ct = cts.Token;

    Coroutine timeoutCoroutine = StartCoroutine(TimeoutChecker(timeout));
    var t = Task.Run(action, ct);
    yield return new WaitWhile(() => t.Status != TaskStatus.RanToCompletion && !timedOut);

    if (timedOut)
    {
        cts.Cancel();
        Debug.Log("Task timed out");
    }            
    else
    {
        StopCoroutine(timeoutCoroutine);
        Debug.Log("Task successfully completed");
    }   
}

private IEnumerator TimeoutChecker(float timeout)
{
    timedOut = false;
    while (timeout > 0)
    {
        timeout -= Time.deltaTime;
        yield return null;
    }
    timedOut = true;
}

Assuming we have the following endless task:

private void MyEndlessTask()
{
    while (true)
    {
        ct.ThrowIfCancellationRequested();
        Debug.Log("Running");
    }
}

With the following usage, the task will run to completion or else it will time out after the default 5 seconds:

StartCoroutine(PerformTimedAction(MyEndlessTask));
Innat3
  • 3,561
  • 2
  • 11
  • 29