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
0
Question by jddegra · Apr 12, 2018 at 07:14 PM · networkingcoroutinescaching

Wait in a coroutine without busy-waiting?

I'm in the middle of implementing a texture caching system and I'd like to implement it through coroutines (since they're a convenient control-flow mechanism for this workflow, and since network requests use IEnumerators). Essentially, my cache keeps three lists. One is the images that it has retrieved that are in the cache currently, another is a list of images that failed to load, and the last is the list of images that it's sent a request for. The workflow should be like this:

1) Request resource X

2) Resource X is in cache/marked as failed? -> Return resource X or failure

3) Resource X is on waiting list? -> Wait until resource X is retrieved, then return resource

4) Send a request for the resource, put it on the waiting list, then return resource

So, this works fine, except for part (3). I can use a WaitUntil to continue checking whether or not the resource is still on the waiting list, but that's busy waiting. I don't want the coroutine to wake up every frame and check the predicate; the better workflow for efficiency would be to keep a reference to every waiting coroutine, then make the coroutine who asked for the resource first to go and wake up every other coroutine that was waiting.

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 · Apr 12, 2018 at 08:11 PM

Well, busy waiting isn't really an issue. Coroutines do not really need to be "woken up". Coroutines are just statemachines. Internally the coroutine scheduler most likely does the same thing as the actual web request is carried out on a seperate thread.


Though a common implementation of loading resources is to use a "loading" class for each resource. If you are happy using an on complete callback you can simply do something like this:

 public class TextureItem
 {
     public enum State { None, Requested, Loaded, Failed }
     public string URL;
     public Texture2D texture;
     public string error;
     public State state;
     List<System.Action<TextureItem>> m_Callbacks = new List<System.Action<TextureItem>>();
     public TextureItem(string aURL)
     {
         state = State.None;
         URL = aURL;
     }
     public void Get(System.Action<TextureItem> aCallback)
     {
         if (state == State.Requested)
             m_Callbacks.Add(aCallback);
         else // in case of error or success, just call the callback directly
             aCallback(this);
     }
     public IEnumerator Load()
     {
         WWW www = new WWW(URL);
         state = State.Requested;
         yield return www;
         if (string.IsNullOrEmpty(www.error))
         {
             texture = www.texture;
             state = State.Loaded;
             foreach(var cb in m_Callbacks)
                 cb(this);
             m_Callbacks.Clear();
         }
         else
         {
             error = www.error;
             state = State.Failed;
         }
     }
 }
 
 public static class TextureCache
 {
     private Dictionary<string, TextureItem> m_Cache = new Dictionary<string, TextureItem>();
     public static void Request(string aURL, System.Action<TextureItem> aCallback)
     {
         TextureItem item;
         if (!m_Cache.TryGetValue(aURL, out item))
         {
             item = new TextureItem(aURL);
             SomeMonobehaviourSingleton.Instance.StartCoroutine(item.Load());
             m_Cache.Add(aURL, item);
         }
         item.Get(aCallback);
     }
 }

Note i quickly hacked this together so there may be some errors ^^. Also note that this requires a monobehaviour singleton in order to run the loading coroutines. Now you can simply request any texture like this:

 TextureCache.Request("http://my.server/some/path", a=>{
     if (a.state == TextureItem.State.Loaded)
         Debug.Log("Texture loaded: " + a.texture.width + " / " + a.texture.height);
     else
         Debug.Log("Some error: " + a.error);
 });

Comment
Add comment · Show 2 · 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 jddegra · Apr 12, 2018 at 08:33 PM 0
Share

The fact that coroutines don't need to be woken up is what I'm trying to avoid here. When you use a yield instruction, Unity has to handle that internally. Is WaitForSecondsRealtime really implemented by setting a timer and waking up every frame to check if the timer has gone off? Why wouldn't it be implemented by yielding, and having the timer itself wake you up when it's done? That's what I'm trying to accomplish here. Waking up every frame makes sense in the context of actions that you'd call in Update() or FixedUpdate(). It does not make sense when trying to use a coroutine as a control flow mechanism; I want it to pause until told to start again.

avatar image Bunny83 jddegra · Apr 12, 2018 at 09:38 PM 0
Share

Do you realise that all scripting code is run on the main thread? That means there has to be some sort of polling going on. $$anonymous$$y example here doesn't do any busy waiting on "our" side, however when you yield a WWW object Unity has to have some sort of check in Update in order to react to a finished download or an expired timer


As i said there is no "waking up" here. A coroutine IEnumerator is just an object with a $$anonymous$$oveNext method. Each time it's called you will move from one yield to the next. Btw: WaitForSecondsRealtime is actually a CustomYieldInstruction which is implemented as seperate coroutine that does exactly this: busy-waiting until "Time.realtimeSinceStartup" is greater than the desired timeout. Note that WaitForSeconds is not a CustomYieldInstruction but is handled internally on the native side. Though there has to be some kind of code that actually does the expire check for all coroutines which are currently "waiting". Of course with a central place in the coroutine scheduler we can do some optimisations. If we sort the waiting coroutine objects based on their expiration time we only have to check the first element each frame even when 100 coroutines are waiting.


Pending web requests can be handled with a threadsafe completion List. So when the actual downloading thread has finished it adds the coroutine back into the update chain. Though we don't know how Unity has implemented the coroutine scheduler on the native side so it's pointless to speculate.


Again, keep in $$anonymous$$d that Coroutines are not threads. It's cooperative multitasking and that involves cooperation of all tasks. Coroutines run on the main thread just like pretty much any other callback you get from Unity (Update, FixedUpdate, On$$anonymous$$ouseDown, ...)


What do you actually mean by "timer"? A PC has an actual timer hardware, however this belongs to the OS as there are very limited timers on our mainboard / chipsets. Almost all "timers" an OS provides to the user are actually some sort of polling timers through software which are based on a single hardware timer which runs at a constant frequency.

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

114 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

Related Questions

[UNet] Best practice to cache components in NetworkBehaviour 0 Answers

Unity networking tutorial? 6 Answers

Characters gun dosent activate on network? 1 Answer

Sending multiple requests at the same time kills performance (FPS 120 -> 9) 0 Answers

AllocatedID error 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