Search Unity

Release Addressables.ResourceManager.CreateChainOperation

Discussion in 'Addressables' started by locbh, Jun 13, 2021.

  1. locbh

    locbh

    Joined:
    Sep 11, 2017
    Posts:
    10
    How can I release the operation with Addressables.ResourceManager.CreateChainOperation ?

    The event is still showing in the Event Viewer.
    I have tried several ways and It is still not working
     
  2. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Bump.

    I'm running into this issue too. After some heavy debugging I found that my asset has an internal ref count of 2 when loaded once therefore I cannot unload it correctly. Calling Addressables.Release twice gives the following error:

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. UnityEditor.AddressableAssets.Diagnostics.Data.EventDataSet.RemoveChild (System.Int32 d) (at Library/PackageCache/com.unity.addressables@1.16.19/Editor/Diagnostics/Data/EventDataSet.cs:115)
    3. UnityEditor.AddressableAssets.Diagnostics.Data.EventDataPlayerSession.HandleOperationDestroy (UnityEngine.ResourceManagement.Diagnostics.DiagnosticEvent evt) (at Library/PackageCache/com.unity.addressables@1.16.19/Editor/Diagnostics/Data/EventDataPlayerSession.cs:250)
    4. UnityEditor.AddressableAssets.Diagnostics.Data.EventDataPlayerSession.Update () (at Library/PackageCache/com.unity.addressables@1.16.19/Editor/Diagnostics/Data/EventDataPlayerSession.cs:205)
    5. UnityEditor.AddressableAssets.Diagnostics.Data.EventDataPlayerSessionCollection.Update () (at Library/PackageCache/com.unity.addressables@1.16.19/Editor/Diagnostics/Data/EventDataPlayerSessionCollection.cs:86)
    6. UnityEditor.AddressableAssets.Diagnostics.GUI.EventViewerWindow.OnInspectorUpdate () (at Library/PackageCache/com.unity.addressables@1.16.19/Editor/Diagnostics/GUI/EventViewerWindow.cs:275)
    7. UnityEditor.HostView.OnInspectorUpdate () (at <42425271a85c48a781b5ae89b9328419>:0)
    If I remove the CreateChainOperation the ref count is correct and I can unload it without problems.

    Here's what I'm trying to do:

    Code (CSharp):
    1. public AsyncOperationHandle<T> LoadAsync<T>(AssetReference asset)
    2. {
    3.     return Addressables.ResourceManager.CreateChainOperation<T, GameObject>(Addressables.LoadAssetAsync<GameObject>(asset), GameObjectReady);
    4. }
    5.  
    6. AsyncOperationHandle<T> GameObjectReady<T>(AsyncOperationHandle<GameObject> arg)
    7. {
    8.     T comp = arg.Result.GetComponent<T>();
    9.     return Addressables.ResourceManager.CreateCompletedOperation(comp, string.Empty);
    10. }
    While this code works without problems, the ref count is not correct and I end up with the chain operation stuck in the event viewer as OP.
     
  3. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    Still cannot figure out this one. Had to switch to use tasks and await for the loading. Can anyone explain how to use ChainOperation correctly?
     
  4. TreyK-47

    TreyK-47

    Unity Technologies

    Joined:
    Oct 22, 2019
    Posts:
    1,822
    I'll ping the team for some guidance.
     
    Rotary-Heart likes this.
  5. unity_shane

    unity_shane

    Unity Technologies

    Joined:
    Jun 3, 2021
    Posts:
    106
    @Rotary-Heart, @locbh, Thank you for bringing this issue to our attention! It looks like this may be an issue related to the fact that it appears that CompletedOperations aren't properly releasing their dependencies leading to refcounts not properly decreasing. In the meantime before we fix the issue, a potential workaround you could do if you wish to continue using ChainOperations/CompletedOperations in this way would be something like this:
    Code (CSharp):
    1. public AsyncOperationHandle<T> LoadAsync<T>(AssetReference asset)
    2.     {
    3.         return Addressables.ResourceManager.CreateChainOperation<T, GameObject>(Addressables.LoadAssetAsync<GameObject>(asset), GameObjectReady<T>);
    4.     }
    5.  
    6.   public AsyncOperationHandle<T> GameObjectReady<T>(AsyncOperationHandle<GameObject> arg)
    7.     {
    8.         T comp = arg.Result.GetComponent<T>();
    9.         var h = Addressables.ResourceManager.CreateCompletedOperation(comp, default);
    10.         h.Destroyed += op => Addressables.Release(op);
    11.         return h;
    12.     }
    Beyond that, in general, I would say that there are also alternative ways to do some of the things that you are doing, for example by using LoadAssetAsync or LoadAssetsAsync and subscribing a function to the AsyncOperationHandle.Completed flag or by using AsyncOperationHandle.WaitForCompletion if you don't mind waiting for the operation to complete. These functions should be more stable and hopefully easier to work with if you'd prefer to avoid directly invoking ChainOperations/CompletedOperations all together.

    Sorry for the hassle and the delay on replying, and I appreciate your patience in waiting. Thank you for bringing this issue to our attention and hopefully we can get a fix in soon.
     
    Denis-535 and Rotary-Heart like this.
  6. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    @unity_shane Thanks for the example. Seems to be working fine. Now I wonder, will this break anything in the future when the fix is implemented in the system?

    As for the alternative ways, for now, I prefer to use the chain operation because it allows me to get the component directly. I've been using the task version for this and I can say that I prefer working with them since it makes the code easier to read.
     
  7. unity_shane

    unity_shane

    Unity Technologies

    Joined:
    Jun 3, 2021
    Posts:
    106
    @Rotary-Heart unfortunately this workaround will break once the fix is implemented on our end. I'll try and make sure that someone messages this thread when we implement the fix so that you have some notice. Once the fix gets implemented reverting the code to the previous version (i.e. the code you originally posted in the thread) should be sufficient to get everything working again. The reason why the fix will break the workaround is because the purpose of the workaround is to release a handle that isn't being properly released by the code, but once the handle is properly released in the code the workaround will be double-releasing the handle, which will cause errors.
     
    Rotary-Heart likes this.
  8. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    813
    @unity_shane Yeah that makes sense, I thought that it would call release twice. It will be amazing if we can get a message here once the fix is implemented.
     
  9. unity_shane

    unity_shane

    Unity Technologies

    Joined:
    Jun 3, 2021
    Posts:
    106
    @Rotary-Heart, Hi there, just wanted to write back in this thread to update you on what's going on on our end. After looking into things more and taking into consideration the different ways that users may end up using CreateChainOperation, we've decided that leaving it how it is and leaving it on the user to release any handles taken up by the ChainOperation is the best way forward. The reason we came to this conclusion is because there are some circumstances where a user may be best served by releasing the secondary handle immediately, while there are others where releasing the handle immediately would break the users code, and there is no good compromise that can be done on the backend that isn't unnecessarily complicated that allows for both of these behaviors. Thus, we will be leaving it as it is now and the "workaround" as we gave it of requiring the user to release the handle manually will be the supported behavior moving forward. Thank you for bringing this specific usage pattern to our attention!
     
    Denis-535 and Rotary-Heart like this.
  10. Flexford

    Flexford

    Joined:
    Dec 8, 2016
    Posts:
    20
    Author of this post asked other question. He release operations, but they didn't hide in Events Viewer.
    I have the same problem now in 2023:)

    Requirements:
    • Unity 2021.3.19
    • Addressables 1.21.10
    Simple code example:
    Code (CSharp):
    1. private static AsyncOperationHandle<float> _floatCompletedOp;
    2. private static AsyncOperationHandle<int> _intChainOp;
    3. private static AsyncOperationHandle<int> _intCompletedOp;
    4.  
    5. private static void TestChainCase()
    6. {
    7.     var rm = Addressables.ResourceManager;
    8.     _floatCompletedOp = rm.CreateCompletedOperation<float>(1.5f, null);
    9.     _intChainOp = rm.CreateChainOperation(_floatCompletedOp, handle =>
    10.     {
    11.         return _intCompletedOp = rm.CreateCompletedOperation<int>((int)handle.Result, null);;
    12.     });
    13.  
    14.     _intChainOp.Completed += handle =>
    15.     {
    16.         Debug.Log(handle.Result);
    17.     };
    18. }
    19.  
    20. private static void TestChainCaseWithAutoRelease()
    21. {
    22.     TestChainCase();
    23.  
    24.     _intChainOp.Completed += handle =>
    25.     {
    26.         Addressables.Release(_intChainOp);
    27.         Addressables.Release(_intCompletedOp);
    28.         Addressables.Release(_floatCompletedOp);
    29.     };
    30. }
    31.  
    32. private static void ReleaseFloatCompletedOp()
    33. {
    34.     Addressables.Release(_floatCompletedOp);
    35. }
    36.  
    37. private static void ReleaseIntCompletedOp()
    38. {
    39.     Addressables.Release(_intCompletedOp);
    40. }
    41.  
    42. private static void ReleaseIntChainOp()
    43. {
    44.     Addressables.Release(_intChainOp);
    45. }
    Results:

    1 - Create operation, method "TestChainCase"


    2 - release chain op, method "ReleaseIntChainOp", result: released chain op and int completed op, but chain op stay in Events Viewer


    3 - release int completed op, method "ReleaseIntCompletedOp", result: exception, because operation already released after release chain op in 2 step



    4 - release float completed op, method "ReleaseFloatCompletedOp", result: released float completed op


    5.1 - try release chain op again, method "ReleaseIntChainOp", result: exception, because chain op already relesed, but continues to stay in Events Viewer!
     

    Attached Files:

    • 1.png
      1.png
      File size:
      42.7 KB
      Views:
      300
    • 2.png
      2.png
      File size:
      37.8 KB
      Views:
      303
    • 3.png
      3.png
      File size:
      81.8 KB
      Views:
      299
    • 4.png
      4.png
      File size:
      37.6 KB
      Views:
      298
    • 5_1.png
      5_1.png
      File size:
      226.3 KB
      Views:
      299
    llFlexford likes this.
  11. Flexford

    Flexford

    Joined:
    Dec 8, 2016
    Posts:
    20

    5.2 - image from editor UI for step 5.1
     

    Attached Files:

    • 5_2.png
      5_2.png
      File size:
      67.5 KB
      Views:
      300
  12. llFlexford

    llFlexford

    Joined:
    Jan 21, 2022
    Posts:
    24
  13. Denis-535

    Denis-535

    Joined:
    Jun 26, 2022
    Posts:
    34
    it's 2024...

    I create the chain operation to covert AsyncOperationHandle<IList<T>> to AsyncOperationHandle<IReadOnlyList<T>>.
    But when I release AsyncOperationHandle<IReadOnlyList<T>> then it doesn't internal AsyncOperationHandle<IList<T>> operation.
    Is this still not fixed? Will this even be fixed?

    Code (CSharp):
    1.  
    2.         public static AsyncOperationHandle<IReadOnlyList<T>> LoadAssetListAsync<T>(string[] keys) where T : UnityEngine.Object {
    3.             return Addressables.LoadAssetsAsync<T>( keys.AsEnumerable(), null, Addressables.MergeMode.Union ).Chain( i => {
    4.                 if (i.IsSucceeded()) {
    5.                     var assets = (IReadOnlyList<T>) i.Result;
    6.                     return Addressables.ResourceManager.CreateCompletedOperation( assets, null );
    7.                 }
    8.                 return Addressables.ResourceManager.CreateCompletedOperationWithException<IReadOnlyList<T>>( null!, i.OperationException );
    9.             } );
    10.         }
    11.  
    12.         private static AsyncOperationHandle<TObject> Chain<TObject, TObjectDependency>(this AsyncOperationHandle<TObjectDependency> handle, Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> operationProvider) {
    13.             return Addressables.ResourceManager.CreateChainOperation( handle, operationProvider );
    14.         }
    15.  
     
  14. Denis-535

    Denis-535

    Joined:
    Jun 26, 2022
    Posts:
    34
    It's clear.
     
  15. Denis-535

    Denis-535

    Joined:
    Jun 26, 2022
    Posts:
    34
    Code (CSharp):
    1.  
    2. private static AsyncOperationHandle<T2> Chain<T1, T2>(this AsyncOperationHandle<T1> handle1, Func<AsyncOperationHandle<T1>, AsyncOperationHandle<T2>> handle2Provider) {
    3.     var handle2 = Addressables.ResourceManager.CreateChainOperation( handle1, handle2Provider );
    4.     handle2.Destroyed += i => Addressables.Release( handle1 );
    5.     return handle2;
    6. }
    7.  
    I hope this will work.