11
\$\begingroup\$

While reading a thread about coroutines, I stumbled upon a random user's comment that coroutines may not fire at all:

Just so you are aware, there is a coroutine limit and if you hit this some coroutines will not fire or fail, leading to very weird and hard to track down behaviour if you have nested coroutines like this.

I've been modelling much of my newer project around coroutines, and the prospect that they may or may not fire would be very harmful, but I cannot find any official documentation about coroutine limitations in Unity.

Other than resource allocation from the fact that you may be constructing things like WaitUntil almost every frame, is there any truth to that comment?

\$\endgroup\$
0

1 Answer 1

24
\$\begingroup\$

I never heard of anything like that. The documentation on coroutines does not mention any of that either. And it also sounds like one of those "If you have to ask, you are doing things wrong" limits to me.

But to prove it, I made a little test setup.

using System.Collections;
using UnityEngine;

public class MassCoroutineTest : MonoBehaviour
{
    private int counter;

    void Update()
    {
        Debug.Log($"{counter} coroutines reporting in!");
        counter = 0;
        if (Input.GetKeyDown(KeyCode.Space)) {
            for (var i = 0; i < 10000; i++) {
                StartCoroutine(TestRoutine());
            }
        }
    }

    IEnumerator TestRoutine() {
        while(true) {
            counter++;
            yield return null;
        }
    }
}

This script generates 10,000 new coroutines whenever you press space. It counts how many of those coroutines actually got executed this frame and outputs the number. I drove this to a million coroutines without the counter ever turning out an uneven number. And then I quit because I only had 1.5 FPS anymore and it got annoyingly unresponsive.

So no, it's a myth. When there is indeed a hard limit on coroutines, you are not going to hit it while still having an acceptable framerate.

But I can imagine how such a myth is created. Game architectures based on lots and lots of coroutines can get really difficult to navigate and debug. You have spooky actions at a distance everywhere and you have no idea how many coroutines are active on which game object and what they are doing. So a codebase that uses a lot of coroutines often becomes really buggy and hard to troubleshoot. But it's always easier to blame the tools you are using than it is to blame yourself for not using them properly. So instead of admitting to themself that they screwed their architecture through over-reliance on coroutines and don't understand it anymore, people go around the Internet and tell everyone it has to be Unity and its coroutine management itself that is buggy.

By the way, I usually use coroutines in my code very rarely. When I want to wait until something happens, then I put the check for that condition into the Update() method of an appropriate component. When I want things to happen in a sequence, then I usually program a simple state machine.


Update: João Mendes in the comments wonders if there might be a limit on nested coroutines. Well, I tried that too by using a recursive coroutine that starts itself:

IEnumerator RecursiveRoutine() {
    counter++;
    yield return StartCoroutine(RecursiveRoutine());
}

This code actually causes a StackOverflowException at a depth of 272 coroutines. But that's a visible error in the console, not just a silent fail. When your game doesn't work and there are exceptions in the console caused by your own code, then that's on you, not on Unity! And it's to be expected that this error happens, because StartCoroutine immediately starts the execution of the coroutine method until the first yield.

However, when you yield for a frame before creating the coroutine, then you no longer have that problem:

IEnumerator RecursiveRoutine() {
    counter++;
    yield return null;
    yield return StartCoroutine(RecursiveRoutine());
}

it seems to run just fine. There is not even an FPS drop. Well, it runs fine, but interestingly the Unity editor crashes occasionally when you let this run for a while and then exit play mode. So recursively calling coroutines might not be such a good idea after all.

\$\endgroup\$
10
  • \$\begingroup\$ I would note that these coroutines you created all run in parallel, while the comment the OP was asking about warned against nested subroutines. \$\endgroup\$ Commented Apr 3, 2023 at 7:57
  • \$\begingroup\$ @JoãoMendes The original claim was that there is a limit to the number of coroutines. But if you can demonstrate an obvious bug in Unity's coroutine handling that occurs only with nested coroutines, be my guest. \$\endgroup\$
    – Philipp
    Commented Apr 3, 2023 at 8:03
  • \$\begingroup\$ I don't think it would be a bug, but rather a nesting limit. Then again, I'm not sure how you'd test it. Maybe yield return TestRoutine() (if that even makes sense, which it might not.) \$\endgroup\$ Commented Apr 3, 2023 at 8:45
  • 2
    \$\begingroup\$ @JoãoMendes Yes, you can have coroutines caling themselves recursively. But when you try to do that as an infinite loop, you are going to get a StackOverflowException. But that should give you a visible exception in the console, and not result in Unity silently just not doing it like described in the Unity Answers post linked in the question. I might try that later when I am back home. \$\endgroup\$
    – Philipp
    Commented Apr 3, 2023 at 8:53
  • 1
    \$\begingroup\$ @JoãoMendes I made some experiments with recursive coroutines. I updated the answer with the results. \$\endgroup\$
    – Philipp
    Commented Apr 3, 2023 at 18:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .