We just ran into a nasty thing with Addressables: AsyncOperationHandle.Task is broken. It returns a Task that either completes successfully or, if there is a failure, just never completes. So basically, if anything goes wrong, your app will just hang.
If the operation is complete by the time you call .Task you’re lucky. You still won’t get a Task that fails but since IsDone also covers failure, at least it will trigger SetResult on the completion source (with a null object).
Task<object> IAsyncOperation.Task
{
get
{
if (m_taskCompletionSourceTypeless == null)
{
m_taskCompletionSourceTypeless = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
if (IsDone && !CompletedEventHasListeners)
m_taskCompletionSourceTypeless.SetResult(Result);
}
return m_taskCompletionSourceTypeless.Task;
}
}
So at least you’re not stuck. However, it’s very unlikely you end up on this path. Most likely when you do .Task, the operation is NOT complete. And then you’re screwed. Because then you end up in
public void Complete(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true)
which does
if (m_Status == AsyncOperationStatus.Failed)
{
if (releaseDependenciesOnFailure)
ReleaseDependencies();
if (m_RM != null && m_RM.postProfilerEvents)
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationFail, 0,
exception?.ToString()));
ICachable cachedOperation = this as ICachable;
if (cachedOperation?.Key != null)
m_RM?.RemoveOperationFromCache(cachedOperation.Key);
RegisterForDeferredCallbackEvent(false);
}
else
{
InvokeCompletionEvent();
DecrementReferenceCount();
}
And bam, you’re screwed. The TaskCompletionSource object won’t ever be touched.
So gist of it is… don’t use AsyncOperationHandle.Task.
This is pretty unfortunate. AsyncOperationHandle.Completed means whatever code you put in there runs from inside Addressables. And since Addressables stuff doesn’t want to be called recursively (it will just error out), it means that if the completion logic ends up triggering some other Addressables operation, you’re screwed again.
PS: We’re using 1.21.20. Haven’t checked whether this is still true for 2.x.