Extended coroutines

Hello all,

Today, at Unity Answers, Fattie posted a question about detecting if coroutine is still running.

I don’t know of any method of detecting this, and I don’t think any simple method exists, apart from setting some variables at coroutine end. Anyway, this triggered my curiosity, so I entered research mode. As a result of my quick R&D, I created StartCoroutineEx extension method, CoroutineController class and CoroutineState enumeration. Please see the code below, but firstly some info.

  1. To use extended version, instead of calling:
StartCoroutine(SomeCoroutine());

you have to use:

CoroutineController controller;
this.StartCoroutineEx(SomeCoroutine(), out controller); // please note that 'this' keyword is required (at least in C#)
  1. Using CoroutineController returned as output parameter, you can check current state of coroutine, as well as control it a bit (stop, pause, resume).

  2. You can yield it as a standard coroutine as well:

yield return this.StartCoroutineEx(SomeCoroutine(), out controller);

A word of explanation is required here. When I started, I planned StartCoroutineEx to return instance of CoroutineEx, where CoroutineEx would be a class derived from YieldInstruction (I was not able to derive from Coroutine, as it is sealed). Everything was working great, except of yielding. After a few tries, I decided it’s a dead end, as this is probably controlled within Unity engine, so returning anything but Coroutine will not be delayed. I went with out parameter then, but if anyone has a different suggestion, please share it.

  1. I used C#, because it’s my almost native language :wink: But if you put my scripts in any of early compilation folder (e.g. Assets\Plugins), then you can use them in UnityScript as well.

  2. This is an alpha version, so if you see an error, let me know. Please let me know as well, what do you think of this idea. Is it good? Or maybe stupid? Or maybe not really stupid, but you would never use it?

Code:

public enum CoroutineState
{
    Ready,
    Running,
    Paused,
    Finished
}
using System.Collections;

public class CoroutineController
{
    private IEnumerator _routine;

    public CoroutineState state;

    public CoroutineController(IEnumerator routine)
    {
        _routine = routine;
        state = CoroutineState.Ready;
    }

    public IEnumerator Start()
    {
        if (state != CoroutineState.Ready)
        {
            throw new System.InvalidOperationException("Unable to start coroutine in state: " + state);
        }

        state = CoroutineState.Running;
        while (_routine.MoveNext())
        {
            yield return _routine.Current;
            while (state == CoroutineState.Paused)
            {
                yield return null;
            }
            if (state == CoroutineState.Finished)
            {
                yield break;
            }
        }

        state = CoroutineState.Finished;
    }

    public void Stop()
    {
        if (state != CoroutineState.Running  state != CoroutineState.Paused)
        {
            throw new System.InvalidOperationException("Unable to stop coroutine in state: " + state);
        }

        state = CoroutineState.Finished;
    }

    public void Pause()
    {
        if (state != CoroutineState.Running)
        {
            throw new System.InvalidOperationException("Unable to pause coroutine in state: " + state);
        }

        state = CoroutineState.Paused;
    }

    public void Resume()
    {
        if (state != CoroutineState.Paused)
        {
            throw new System.InvalidOperationException("Unable to resume coroutine in state: " + state);
        }

        state = CoroutineState.Running;
    }
}
using UnityEngine;
using System.Collections;

public static class CoroutineExtensions
{
    public static Coroutine StartCoroutineEx(this MonoBehaviour monoBehaviour, IEnumerator routine, out CoroutineController coroutineController)
    {
        if (routine == null)
        {
            throw new System.ArgumentNullException("routine");
        }

        coroutineController = new CoroutineController(routine);
        return monoBehaviour.StartCoroutine(coroutineController.Start());
    }
}
2 Likes

Looks amazing, will investigate.

Could you wrap a normal Coroutine in a static class which keeps a list of currently running Coroutines, and use that list to see what’s currently running?

http://forum.unity3d.com/threads/94220-A-more-flexible-coroutine-interface
but i like your approach of extension method. will have an eye on this. thanks for sharing it.

If you mean standard Coroutine (as an object returned from StartCoroutine), then I don’t think you can do this, as there is no way to tell when it finished. You can of course keep reference to original IEnumerator passed to StartCoroutine, and check its Current property, but I think it’s not a good approach.

Thank you for this link! I haven’t seen it before, but in fact I haven’t been searching for anything… :sweat_smile:

Anyway - my approach is a bit different, as it allows you to work with coroutines in a way similar to using standard coroutines (e.g. yielding). But I like the code from linked thread (especially finished notification event). I’ll update my code if there will be a demand for this :wink:

Great mate, just what I was looking for. Works perfectly. I needed to start a couple of coroutines with different durations all at once, and when all are finished to call an event. Thank you very much.

if you dont mind the code overhead (or if you already use it), I would recomend to include the parse plugin.

It implements System.Threading.Tasks which is VERY useful for concurrency

Uhm, as far as I know the System.Threading.Tasks namespace is only available in net 4.0 and higher. (In case you dont know, unity uses a mono version which is a counterpart to .net 3.5, give or take)

You are aware they don’t run in parallel, right? They are still all monothreaded, and ran one after the other.

1 Like

Yes I know, but it somehow simulates multithreading . I mean, the coroutines I start all together, are not with equal duration, and I don’t know which one is actually the longest, but I need to trigger the event when all are finished. This code really helped me. I am gonna edit the post, so I don’t confuse the people. Thanks for the note.

Where I can get this plugin from? And isn’t Unity’s API not thread safe?

“not safe” is an understatement. You cannot access or modify any of Unity’s object or method from any thread except the main one. Exception to this are structs that are fully on the managed side, like Vectors, Quaternion, Color…

1 Like

I’m glad you found the code useful :slight_smile:

Wouldn’t yielding in some other coroutine not accomplish the same thing?

IEnumerator TheYielder(IEnumerator me)
{
  while(me.moveNext())
  {
    //me Still active
     yield return null;
  }
}

Andrew - not exactly. Your code would work, if you yielded original enumerator value instead of null (unless MoveNext returns false). But the whole point of my code was to introduce a wrapper, which allows you to easily control coroutines. So apart from checking if it is still running, you can pause, resume, etc.

So I can do some background calculations, but if I want to change the scale of an object I won’t be able to do it?

Exact!

1 Like

No, they aswell dont “somehow simultate multithreading”. I know what you want to say but its simply like comparing apples with steaks. Coroutines are method’s which are paused and resumed lateron. Its nothing even similar to multithreading.
When multithreading a method, it might aswell be paused and resumed, but you do that by pausing the whole thread without having anything inbetween. Thats still not the point of multithreading, but instead moving a method from your operating mainthread to another thread, most likely on another core, so not consuming any of your mainthreads time. This is real parallism (as long as the method runs on another core of course). Whereas coroutines always run on the same thread, just because of the fact of them being able to be paused, doesnt make them anywhere similar to multithreading.

My opinion. -

I know, but Parse’s plugin implements it and works great
(I used to use it a lot in .NET development and I was missing it in unity)

Unity asset store, search “Parse” and download their official plugin (you dont even need a parse account)

As they told you the limitation is to call unity’s objects from the main thread, but switching to the main thread is very easy (if using Tasks), I can post some example code if you are interested