AsyncOperationBase.Task implementation

Hi! current AsyncOperationBase.Task property implementation use WaitHandler and start completely new thread just to wait on it. This implementation is far not optimal because of wasting thread from thread pool for blocking wait.
Current implementation:

        System.Threading.EventWaitHandle m_waitHandle;
        internal System.Threading.WaitHandle WaitHandle
        {
            get
            {
                if (m_waitHandle == null)
                    m_waitHandle = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
                m_waitHandle.Reset();
                return m_waitHandle;
            }
        }
        internal System.Threading.Tasks.Task<TObject> Task
        {
            get
            {
#if UNITY_WEBGL
                Debug.LogError("Multithreaded operation are not supported on WebGL.  Unable to aquire Task.");
                return default;
#else
                if (Status == AsyncOperationStatus.Failed)
                {
                    return System.Threading.Tasks.Task.FromResult(default(TObject));
                }
                if (Status == AsyncOperationStatus.Succeeded)
                {
                    return System.Threading.Tasks.Task.FromResult(Result);
                }

                var handle = WaitHandle;
                return System.Threading.Tasks.Task.Factory.StartNew((Func<object, TObject>)(o =>
                {
                    var asyncOperation = o as AsyncOperationBase<TObject>;
                    if (asyncOperation == null)
                        return default(TObject);
                    handle.WaitOne();
                    return (TObject)asyncOperation.Result;
                }), this);
#endif
            }

        
        internal void InvokeCompletionEvent()
        {
            if (m_CompletedActionT != null)
            {
                m_CompletedActionT.Invoke(new AsyncOperationHandle<TObject>(this));
                m_CompletedActionT.Clear();
            }

            if (m_waitHandle != null)
                m_waitHandle.Set();

            m_InDeferredCallbackQueue = false;
        }
        }

What do you think about changing implementation to use TaskComletionSource? With it code above could be rewrite to something like this

        System.Threading.Tasks.TaskCompletionSource<TObject> m_taskCompletionSource;
        internal System.Threading.Tasks.TaskCompletionSource<TObject> TaskCompletionSource
        {
            get
            {
                if (m_taskCompletionSource == null)
                    m_taskCompletionSource = new System.Threading.Tasks.TaskCompletionSource<TObject>();
               
                return m_taskCompletionSource;
            }
        }

        internal System.Threading.Tasks.Task<TObject> Task
        {
            get
            {
                if (Status == AsyncOperationStatus.Failed)
                {
                    return System.Threading.Tasks.Task.FromResult(default(TObject));
                }
                if (Status == AsyncOperationStatus.Succeeded)
                {
                    return System.Threading.Tasks.Task.FromResult(Result);
                }

                return TaskCompletionSource.Task;
            }
        }

        internal void InvokeCompletionEvent()
        {
            if (m_CompletedActionT != null)
            {
                m_CompletedActionT.Invoke(new AsyncOperationHandle<TObject>(this));
                m_CompletedActionT.Clear();
            }

            if (m_taskCompletionSource != null)
                m_taskCompletionSource.SetResult(Result);

            m_InDeferredCallbackQueue = false;
        }

Pros: no threads wasting, reusing task instance, work on WebGL(no additional threads involved)
Cons: personally don’t see any

2 Likes

@davidla_unity , Any thoughts about this?

This is a problem indeed. This is one of the reasons we have to avoid using Tasks with addressables in our project. Another issue is no support for cancellation tokens.

I’ll flag with the team for some guidance!

1 Like

Hm, good idea. I’ll make a ticket for us to evaluate this and see what we can do. Thanks for the solution suggestion, I really appreciate it. This is definitely worth investigating.

1 Like

+1
can you please add something to cancel or abort while downloading the addressables remotely .