From 36ac0863adc827eb14389f9c91120a6d3d8aa88e Mon Sep 17 00:00:00 2001 From: Luciano Prestes Cavalcanti Date: Wed, 22 Nov 2023 08:09:03 -0300 Subject: [PATCH 1/3] Release handle when cancellation is requested --- .../Addressables/AddressablesAsyncExtensions.cs | 17 +++++++++++++++++ .../Addressables/UniTask.Addressables.asmdef | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs index a0ca8a1..0f3a25c 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs @@ -7,6 +7,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; +using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; namespace Cysharp.Threading.Tasks @@ -171,6 +172,10 @@ namespace Cysharp.Threading.Tasks completed = true; if (cancellationToken.IsCancellationRequested) { + If (handle.IsValid()) + { + Addressables.Release(handle); + } core.TrySetCanceled(cancellationToken); } else if (handle.Status == AsyncOperationStatus.Failed) @@ -215,6 +220,10 @@ namespace Cysharp.Threading.Tasks if (cancellationToken.IsCancellationRequested) { completed = true; + if (handle.IsValid()) + { + Addressables.Release(handle); + } core.TrySetCanceled(cancellationToken); return false; } @@ -353,6 +362,10 @@ namespace Cysharp.Threading.Tasks completed = true; if (cancellationToken.IsCancellationRequested) { + if (handle.IsValid()) + { + Addressables.Release(handle); + } core.TrySetCanceled(cancellationToken); } else if (argHandle.Status == AsyncOperationStatus.Failed) @@ -402,6 +415,10 @@ namespace Cysharp.Threading.Tasks if (cancellationToken.IsCancellationRequested) { completed = true; + if (handle.IsValid()) + { + Addressables.Release(handle); + } core.TrySetCanceled(cancellationToken); return false; } diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef index 0439df7..faed8ec 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/UniTask.Addressables.asmdef @@ -2,7 +2,8 @@ "name": "UniTask.Addressables", "references": [ "UniTask", - "Unity.ResourceManager" + "Unity.ResourceManager", + "Unity.Addressables" ], "includePlatforms": [], "excludePlatforms": [], From ee12dd9ae7dc012092bd27f78e15e6dbb101074e Mon Sep 17 00:00:00 2001 From: hadashiA Date: Thu, 25 Jan 2024 17:47:06 +0900 Subject: [PATCH 2/3] Fix omissions in Addressable.Release and IsValid checks --- .../AddressablesAsyncExtensions.cs | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs index 0f3a25c..55de21e 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs @@ -109,7 +109,7 @@ namespace Cysharp.Threading.Tasks TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size); } - readonly Action continuationAction; + readonly Action completedCallback; AsyncOperationHandle handle; CancellationToken cancellationToken; CancellationTokenRegistration cancellationTokenRegistration; @@ -120,7 +120,7 @@ namespace Cysharp.Threading.Tasks AsyncOperationHandleConfiguredSource() { - continuationAction = Continuation; + completedCallback = HandleCompleted; } public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, out short token) @@ -145,6 +145,10 @@ namespace Cysharp.Threading.Tasks result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => { var promise = (AsyncOperationHandleConfiguredSource)state; + if (promise.handle.IsValid()) + { + Addressables.Release(promise.handle); + } promise.core.TrySetCanceled(promise.cancellationToken); }, result); } @@ -153,15 +157,18 @@ namespace Cysharp.Threading.Tasks PlayerLoopHelper.AddAction(timing, result); - handle.Completed += result.continuationAction; + handle.Completed += result.completedCallback; token = result.core.Version; return result; } - void Continuation(AsyncOperationHandle _) + void HandleCompleted(AsyncOperationHandle _) { - handle.Completed -= continuationAction; + if (handle.IsValid()) + { + handle.Completed -= completedCallback; + } if (completed) { @@ -172,7 +179,7 @@ namespace Cysharp.Threading.Tasks completed = true; if (cancellationToken.IsCancellationRequested) { - If (handle.IsValid()) + if (handle.IsValid()) { Addressables.Release(handle); } @@ -299,7 +306,7 @@ namespace Cysharp.Threading.Tasks TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size); } - readonly Action> continuationAction; + readonly Action> completedCallback; AsyncOperationHandle handle; CancellationToken cancellationToken; CancellationTokenRegistration cancellationTokenRegistration; @@ -310,7 +317,7 @@ namespace Cysharp.Threading.Tasks AsyncOperationHandleConfiguredSource() { - continuationAction = Continuation; + completedCallback = HandleCompleted; } public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, out short token) @@ -335,6 +342,10 @@ namespace Cysharp.Threading.Tasks result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => { var promise = (AsyncOperationHandleConfiguredSource)state; + if (promise.handle.IsValid()) + { + Addressables.Release(promise.handle); + } promise.core.TrySetCanceled(promise.cancellationToken); }, result); } @@ -343,15 +354,18 @@ namespace Cysharp.Threading.Tasks PlayerLoopHelper.AddAction(timing, result); - handle.Completed += result.continuationAction; + handle.Completed += result.completedCallback; token = result.core.Version; return result; } - void Continuation(AsyncOperationHandle argHandle) + void HandleCompleted(AsyncOperationHandle argHandle) { - handle.Completed -= continuationAction; + if (handle.IsValid()) + { + handle.Completed -= completedCallback; + } if (completed) { From b99646558c3a144c8a83923c61f9bf45b1a8322c Mon Sep 17 00:00:00 2001 From: hadashiA Date: Thu, 25 Jan 2024 18:59:15 +0900 Subject: [PATCH 3/3] Add a flag autoReleaseWhenCancelled to addressable extensionhs --- .../AddressablesAsyncExtensions.cs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs index 55de21e..656e833 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs @@ -21,17 +21,12 @@ namespace Cysharp.Threading.Tasks return ToUniTask(handle).GetAwaiter(); } - public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken) + public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCancelled = false) { - return ToUniTask(handle, cancellationToken: cancellationToken); + return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCancelled: autoReleaseWhenCancelled); } - public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately) - { - return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately); - } - - public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false) + public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCancelled = false) { if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); @@ -50,7 +45,7 @@ namespace Cysharp.Threading.Tasks return UniTask.CompletedTask; } - return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, out var token), token); + return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCancelled, out var token), token); } public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion @@ -114,6 +109,7 @@ namespace Cysharp.Threading.Tasks CancellationToken cancellationToken; CancellationTokenRegistration cancellationTokenRegistration; IProgress progress; + bool autoReleaseWhenCancelled; bool completed; UniTaskCompletionSourceCore core; @@ -123,7 +119,7 @@ namespace Cysharp.Threading.Tasks completedCallback = HandleCompleted; } - public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, out short token) + public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCancelled, out short token) { if (cancellationToken.IsCancellationRequested) { @@ -139,13 +135,14 @@ namespace Cysharp.Threading.Tasks result.progress = progress; result.cancellationToken = cancellationToken; result.completed = false; + result.autoReleaseWhenCancelled = autoReleaseWhenCancelled; if (cancelImmediately && cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => { var promise = (AsyncOperationHandleConfiguredSource)state; - if (promise.handle.IsValid()) + if (promise.autoReleaseWhenCancelled && promise.handle.IsValid()) { Addressables.Release(promise.handle); } @@ -179,7 +176,7 @@ namespace Cysharp.Threading.Tasks completed = true; if (cancellationToken.IsCancellationRequested) { - if (handle.IsValid()) + if (autoReleaseWhenCancelled && handle.IsValid()) { Addressables.Release(handle); } @@ -227,7 +224,7 @@ namespace Cysharp.Threading.Tasks if (cancellationToken.IsCancellationRequested) { completed = true; - if (handle.IsValid()) + if (autoReleaseWhenCancelled && handle.IsValid()) { Addressables.Release(handle); } @@ -264,17 +261,12 @@ namespace Cysharp.Threading.Tasks return ToUniTask(handle).GetAwaiter(); } - public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken) + public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCancelled = false) { - return ToUniTask(handle, cancellationToken: cancellationToken); + return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCancelled: autoReleaseWhenCancelled); } - public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately) - { - return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately); - } - - public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false) + public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCancelled = false) { if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); @@ -292,7 +284,7 @@ namespace Cysharp.Threading.Tasks return UniTask.FromResult(handle.Result); } - return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, out var token), token); + return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCancelled, out var token), token); } sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode> @@ -311,6 +303,7 @@ namespace Cysharp.Threading.Tasks CancellationToken cancellationToken; CancellationTokenRegistration cancellationTokenRegistration; IProgress progress; + bool autoReleaseWhenCancelled; bool completed; UniTaskCompletionSourceCore core; @@ -320,7 +313,7 @@ namespace Cysharp.Threading.Tasks completedCallback = HandleCompleted; } - public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, out short token) + public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCancelled, out short token) { if (cancellationToken.IsCancellationRequested) { @@ -336,13 +329,14 @@ namespace Cysharp.Threading.Tasks result.cancellationToken = cancellationToken; result.completed = false; result.progress = progress; + result.autoReleaseWhenCancelled = autoReleaseWhenCancelled; if (cancelImmediately && cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => { var promise = (AsyncOperationHandleConfiguredSource)state; - if (promise.handle.IsValid()) + if (promise.autoReleaseWhenCancelled && promise.handle.IsValid()) { Addressables.Release(promise.handle); } @@ -376,7 +370,7 @@ namespace Cysharp.Threading.Tasks completed = true; if (cancellationToken.IsCancellationRequested) { - if (handle.IsValid()) + if (autoReleaseWhenCancelled && handle.IsValid()) { Addressables.Release(handle); } @@ -429,7 +423,7 @@ namespace Cysharp.Threading.Tasks if (cancellationToken.IsCancellationRequested) { completed = true; - if (handle.IsValid()) + if (autoReleaseWhenCancelled && handle.IsValid()) { Addressables.Release(handle); }