Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by pwemartinez · Mar 03, 2020 at 08:08 PM · yieldcoroutinesstopcoroutine

Inconsistent StopCoroutine behavior with CustomYieldInstruction

I noticed that I tried to use StopCoroutine on a coroutine that was waiting on a CustomYieldInstruction and that coroutine did not stop. The behavior I am seeing is inconsistent: most times the coroutine stops properly. However, if I am yielding on a series of nested IEnumerators, then the coroutine runs until the outer IEnumerator.

Does anyone have insights about why this is happening? It looks to me like a unity bug, but maybe I am misunderstanding something fundamental.

Here's a test monobehavior. It "should" print 3 lines "Starting Coroutine" "TestRoutine Started" "Stopping Coroutine". However, it prints 7 lines "Starting Coroutine" "TestRoutine Started" "Stopping Coroutine" "Post CustomYield" "PostCustomYield2" "PostCustomYield3" "PostCustomYield4"

 using System;
 using System.Collections;
 using UnityEngine;
 
 public class CustomYieldTest : MonoBehaviour
 {
     private bool _testStarted;
     private Coroutine _coroutine;
     public bool _stopMe;
 
     public void Update()
     {
         if (!_testStarted)
         {
             _testStarted = true;
             Debug.LogWarning($"Starting Coroutine {Time.frameCount}");
             _coroutine = StartCoroutine(TestRoutine());
         }
         else if (_stopMe)
         {
             Debug.LogWarning($"Stopping Coroutine {Time.frameCount}");
             StopCoroutine(_coroutine);
             _stopMe = false;
         }
     }
 
     private IEnumerator TestRoutine()
     {
         Debug.LogWarning($"TestRoutine Started {Time.frameCount}");
         //Change this to use TestRoutineInternal2 and none of the "Post CustomYield" logs occur
         yield return TestRoutineInternal(this); 
         Debug.LogWarning($"Post CustomYield5 {Time.frameCount}");
         yield return null;
         Debug.LogWarning($"Post CustomYield6 {Time.frameCount}");
     }
     
     private IEnumerator TestRoutineInternal(CustomYieldTest parent)
     {
         yield return TestRoutineInternal2(parent);
         Debug.LogWarning($"Post CustomYield3 {Time.frameCount}");
         yield return null;
         Debug.LogWarning($"Post CustomYield4 {Time.frameCount}");
     }
     
     private IEnumerator TestRoutineInternal2(CustomYieldTest parent)
     {
         parent._stopMe = true;
         //Change this to 'yield return null;' and none of the "Post CustomYield" logs occur
         yield return new CustomYieldImplementation(1f);
         Debug.LogWarning($"Post CustomYield {Time.frameCount}");
         yield return null;
         Debug.LogWarning($"Post CustomYield2 {Time.frameCount}");
     }
 }
 
 public class CustomYieldImplementation : CustomYieldInstruction
 {
     private DateTime _timeToStop;
 
     public CustomYieldImplementation(float seconds)
     {
         _timeToStop = DateTime.Now.AddSeconds(seconds);
     }
 
     public override bool keepWaiting => DateTime.Now < _timeToStop;
 }

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by Bunny83 · Mar 04, 2020 at 02:04 AM

Ok I just have rewritten my entire answer since there were some mistakes in it ^^.I'm not sure if it always was like this since Unity supported yielding on IEnumerators. However currently it seems that yielding an IEnumerator will not start a new nested coroutine. Instead the coroutine scheduler seems to just chain the statemachines in the same coroutine. So you still have only one coroutine. When you yield on a nested IEnumerator the coroutine probably just stores the IEnumerator internally as the current active one (they might use a stack for that).


I just ran your test with both, your custom yield instruction and just yielding null. However it doesn't change the behaviour at all. I don't get any of your "Post CustomYield" logs since in the very first run you immediately set your "_stopMe" variable to true your coroutine will be stopped the next frame when Update runs. So your coroutine will never get past the first yield statement. Once the coroutine is stopped it will just vanish.


I can not reproduce your output given your code. Maybe you had your parent._stopMe = true; line originally after your first yield statement? However in this case you could only see the first "Post CustomYield" inside your "TestRoutineInternal2". After that your coroutine would be stopped and nothing else could actually execute. Again I don't see any change when I replace your yield return new CustomYieldImplementation(1f); with yield return null;. If the _stopMe line is after that statement the only difference is that it takes 1 second before the coroutine is stopped when your custom yield instruction is used.


Note I carried out my tests in Unity 2019.0.3f6. Maybe you use a different Unity version?

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image pwemartinez · Mar 04, 2020 at 03:08 AM 0
Share

Wow. I did not know that. I'm still a bit confused though.

Why if I change line "yield return new CustomYieldImplementation(1f);" to "yield return null;" does it not print any of the "Post CustomYield" lines? Shouldn't it still print the 4 "Post CustomYield" lines?

avatar image Bunny83 pwemartinez · Mar 04, 2020 at 02:49 PM 0
Share

Note that I've run some tests with your code and I have re-written my answer since the behaviour might have changed in the past.

avatar image pwemartinez · Mar 04, 2020 at 06:28 PM 0
Share

$$anonymous$$y original test was on 2018.4.3f1LTS. I confirmed once again using this code there prints 4 "Post CustomYield" statements.

The exact same code on 2019.3.3f1 prints 0 "Post CustomYield" statements.

It appears unity does not (generally) start separate coroutines for yielding on nested IEnumerators, even in 2018.4. I had to work hard to get an example that prints the "Post CustomYield" statements: if you remove one layer of nesting it does not work; if you yield return null, it does not work. To my eyes this looks like an inconsistency in 2018.4, I can not explain why it behaves like its starting separate coroutines only in this very specific instance.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

127 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Problem with Stopping Nested Coroutines: Control Never Returned to Parent 3 Answers

Stuck while solving a Coroutine problem 0 Answers

Optimizing coroutines? 2 Answers

Mysteries of yield 1 Answer

Using coroutine as Update(). Waiting until the button is pressed. 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges