Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

WaitForJobCompleted: custom yield instruction that waits for a JobHandle

Discussion in 'Entity Component System' started by CodeSmile, May 14, 2022.

  1. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,196
    I just realized that we can write custom yield instructions for coroutines. I'd like to share my implementation of WaitForJobCompleted that takes a JobHandle and ends the coroutine if the handle is completed.

    By default, job completion is forced after 4 frames to prevent TempJob allocators from complaining. Pass false as the second parameter if you have a long-running job with a Persistent allocator.
    Code (CSharp):
    1. public class WaitForJobCompleted : CustomYieldInstruction
    2. {
    3.     private readonly bool _isUsingTempJobAllocator;
    4.     private readonly int _forceJobCompletionFrameCount;
    5.     private JobHandle _jobHandle;
    6.  
    7.     public override bool keepWaiting
    8.     {
    9.         get
    10.         {
    11.             if (_isUsingTempJobAllocator && Time.frameCount >= _forceJobCompletionFrameCount)
    12.                 _jobHandle.Complete();
    13.            // optional: automatically call Complete() on the handle after it completed
    14.            // side note: yes, you have to complete what has already completed, i still think that's bonkers! :)
    15.            // if (_jobHandle.IsCompleted) _jobHandle.Complete();
    16.             return _jobHandle.IsCompleted == false;
    17.         }
    18.     }
    19.  
    20.     public WaitForJobCompleted(JobHandle jobHandle, bool isUsingTempJobAllocator = true)
    21.     {
    22.         _jobHandle = jobHandle;
    23.         _isUsingTempJobAllocator = isUsingTempJobAllocator;
    24.  
    25.         // force completion before running into native Allocator.TempJob's lifetime limit of 4 frames
    26.         _forceJobCompletionFrameCount = Time.frameCount + 4;
    27.     }
    28. }
    Here's an example coroutine using WaitForJobCompleted which invokes an Action with the result (in this case: a Mesh instance):
    Code (CSharp):
    1. StartCoroutine(WaitForMeshCompleted(mesh => { meshFilter.sharedMesh = mesh; }));
    Code (CSharp):
    1. public IEnumerator WaitForMeshCompleted(Action<Mesh> callback)
    2. {
    3.     // check first whether job has already been completed within the current frame!
    4.     // if not completed this frame, just wait till it does complete (at most 4 frames)
    5.     if (IsCompleted() == false)
    6.         yield return new WaitForJobCompleted(_buildJobHandle, isUsingTempJobAllocator: true);
    7.  
    8.     callback.Invoke(GetMesh());
    9. }
     
    Last edited: Aug 13, 2022
  2. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    949
    I have tried to use this, but getting error:

    InvalidOperationException: The previously scheduled job OrientedBounds:BoundsJob writes to the Unity.Collections.NativeArray`1[UnityEngine.Bounds] BoundsJob.result. You must call JobHandle.Complete() on the job OrientedBounds:BoundsJob, before you can read from the Unity.Collections.NativeArray`1[UnityEngine.Bounds] safely.

    - when trying to access the job result


    But the coroutine fhom here: https://innomin8.dev/blog/unity-c-job-system-and-coroutines runs fine.
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,196
    The WaitForJobCompleted coroutine isn't calling Complete() on the JobHandle. Did you call handle.Complete() after the yield instruction and before accessing job data? If not that would explain the issue. Otherwise please post some more code for analysis, thanks!

    PS: updated the code to automatically call Complete() when the handle is completed. Couldn't test it therefore I only added it as a comment which you'll need to uncomment to try this.
     
    Last edited: Aug 13, 2022
    tomekkie2 likes this.
  4. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    949
    Thank you.
    That fixes it.
     
    CodeSmile likes this.