AddressablesAsyncExtensions.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // asmdef Version Defines, enabled when com.unity.addressables is imported.
  2. #if UNITASK_ADDRESSABLE_SUPPORT
  3. using Cysharp.Threading.Tasks.Internal;
  4. using System;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.ExceptionServices;
  7. using System.Threading;
  8. using UnityEngine.ResourceManagement.AsyncOperations;
  9. namespace Cysharp.Threading.Tasks
  10. {
  11. public static class AddressablesAsyncExtensions
  12. {
  13. #region AsyncOperationHandle
  14. public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
  15. {
  16. return ToUniTask(handle).GetAwaiter();
  17. }
  18. public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken)
  19. {
  20. return ToUniTask(handle, cancellationToken: cancellationToken);
  21. }
  22. public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
  23. {
  24. if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
  25. if (!handle.IsValid())
  26. {
  27. // autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed.
  28. return UniTask.CompletedTask;
  29. }
  30. if (handle.IsDone)
  31. {
  32. if (handle.Status == AsyncOperationStatus.Failed)
  33. {
  34. return UniTask.FromException(handle.OperationException);
  35. }
  36. return UniTask.CompletedTask;
  37. }
  38. return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, out var token), token);
  39. }
  40. public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
  41. {
  42. AsyncOperationHandle handle;
  43. Action<AsyncOperationHandle> continuationAction;
  44. public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
  45. {
  46. this.handle = handle;
  47. this.continuationAction = null;
  48. }
  49. public bool IsCompleted => handle.IsDone;
  50. public void GetResult()
  51. {
  52. if (continuationAction != null)
  53. {
  54. handle.Completed -= continuationAction;
  55. continuationAction = null;
  56. }
  57. if (handle.Status == AsyncOperationStatus.Failed)
  58. {
  59. var e = handle.OperationException;
  60. handle = default;
  61. ExceptionDispatchInfo.Capture(e).Throw();
  62. }
  63. var result = handle.Result;
  64. handle = default;
  65. }
  66. public void OnCompleted(Action continuation)
  67. {
  68. UnsafeOnCompleted(continuation);
  69. }
  70. public void UnsafeOnCompleted(Action continuation)
  71. {
  72. Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
  73. continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
  74. handle.Completed += continuationAction;
  75. }
  76. }
  77. sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
  78. {
  79. static TaskPool<AsyncOperationHandleConfiguredSource> pool;
  80. AsyncOperationHandleConfiguredSource nextNode;
  81. public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
  82. static AsyncOperationHandleConfiguredSource()
  83. {
  84. TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
  85. }
  86. readonly Action<AsyncOperationHandle> continuationAction;
  87. AsyncOperationHandle handle;
  88. CancellationToken cancellationToken;
  89. IProgress<float> progress;
  90. bool completed;
  91. UniTaskCompletionSourceCore<AsyncUnit> core;
  92. AsyncOperationHandleConfiguredSource()
  93. {
  94. continuationAction = Continuation;
  95. }
  96. public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
  97. {
  98. if (cancellationToken.IsCancellationRequested)
  99. {
  100. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  101. }
  102. if (!pool.TryPop(out var result))
  103. {
  104. result = new AsyncOperationHandleConfiguredSource();
  105. }
  106. result.handle = handle;
  107. result.progress = progress;
  108. result.cancellationToken = cancellationToken;
  109. result.completed = false;
  110. TaskTracker.TrackActiveTask(result, 3);
  111. PlayerLoopHelper.AddAction(timing, result);
  112. handle.Completed += result.continuationAction;
  113. token = result.core.Version;
  114. return result;
  115. }
  116. void Continuation(AsyncOperationHandle _)
  117. {
  118. handle.Completed -= continuationAction;
  119. if (completed)
  120. {
  121. TryReturn();
  122. }
  123. else
  124. {
  125. completed = true;
  126. if (cancellationToken.IsCancellationRequested)
  127. {
  128. core.TrySetCanceled(cancellationToken);
  129. }
  130. else if (handle.Status == AsyncOperationStatus.Failed)
  131. {
  132. core.TrySetException(handle.OperationException);
  133. }
  134. else
  135. {
  136. core.TrySetResult(AsyncUnit.Default);
  137. }
  138. }
  139. }
  140. public void GetResult(short token)
  141. {
  142. core.GetResult(token);
  143. }
  144. public UniTaskStatus GetStatus(short token)
  145. {
  146. return core.GetStatus(token);
  147. }
  148. public UniTaskStatus UnsafeGetStatus()
  149. {
  150. return core.UnsafeGetStatus();
  151. }
  152. public void OnCompleted(Action<object> continuation, object state, short token)
  153. {
  154. core.OnCompleted(continuation, state, token);
  155. }
  156. public bool MoveNext()
  157. {
  158. if (completed)
  159. {
  160. TryReturn();
  161. return false;
  162. }
  163. if (cancellationToken.IsCancellationRequested)
  164. {
  165. completed = true;
  166. core.TrySetCanceled(cancellationToken);
  167. return false;
  168. }
  169. if (progress != null && handle.IsValid())
  170. {
  171. progress.Report(handle.PercentComplete);
  172. }
  173. return true;
  174. }
  175. bool TryReturn()
  176. {
  177. TaskTracker.RemoveTracking(this);
  178. core.Reset();
  179. handle = default;
  180. progress = default;
  181. cancellationToken = default;
  182. return pool.TryPush(this);
  183. }
  184. }
  185. #endregion
  186. #region AsyncOperationHandle_T
  187. public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
  188. {
  189. return ToUniTask(handle).GetAwaiter();
  190. }
  191. public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken)
  192. {
  193. return ToUniTask(handle, cancellationToken: cancellationToken);
  194. }
  195. public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
  196. {
  197. if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
  198. if (!handle.IsValid())
  199. {
  200. throw new Exception("Attempting to use an invalid operation handle");
  201. }
  202. if (handle.IsDone)
  203. {
  204. if (handle.Status == AsyncOperationStatus.Failed)
  205. {
  206. return UniTask.FromException<T>(handle.OperationException);
  207. }
  208. return UniTask.FromResult(handle.Result);
  209. }
  210. return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, out var token), token);
  211. }
  212. sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
  213. {
  214. static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
  215. AsyncOperationHandleConfiguredSource<T> nextNode;
  216. public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
  217. static AsyncOperationHandleConfiguredSource()
  218. {
  219. TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
  220. }
  221. readonly Action<AsyncOperationHandle<T>> continuationAction;
  222. AsyncOperationHandle<T> handle;
  223. CancellationToken cancellationToken;
  224. IProgress<float> progress;
  225. bool completed;
  226. UniTaskCompletionSourceCore<T> core;
  227. AsyncOperationHandleConfiguredSource()
  228. {
  229. continuationAction = Continuation;
  230. }
  231. public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
  232. {
  233. if (cancellationToken.IsCancellationRequested)
  234. {
  235. return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
  236. }
  237. if (!pool.TryPop(out var result))
  238. {
  239. result = new AsyncOperationHandleConfiguredSource<T>();
  240. }
  241. result.handle = handle;
  242. result.cancellationToken = cancellationToken;
  243. result.completed = false;
  244. result.progress = progress;
  245. TaskTracker.TrackActiveTask(result, 3);
  246. PlayerLoopHelper.AddAction(timing, result);
  247. handle.Completed += result.continuationAction;
  248. token = result.core.Version;
  249. return result;
  250. }
  251. void Continuation(AsyncOperationHandle<T> argHandle)
  252. {
  253. handle.Completed -= continuationAction;
  254. if (completed)
  255. {
  256. TryReturn();
  257. }
  258. else
  259. {
  260. completed = true;
  261. if (cancellationToken.IsCancellationRequested)
  262. {
  263. core.TrySetCanceled(cancellationToken);
  264. }
  265. else if (argHandle.Status == AsyncOperationStatus.Failed)
  266. {
  267. core.TrySetException(argHandle.OperationException);
  268. }
  269. else
  270. {
  271. core.TrySetResult(argHandle.Result);
  272. }
  273. }
  274. }
  275. public T GetResult(short token)
  276. {
  277. return core.GetResult(token);
  278. }
  279. void IUniTaskSource.GetResult(short token)
  280. {
  281. GetResult(token);
  282. }
  283. public UniTaskStatus GetStatus(short token)
  284. {
  285. return core.GetStatus(token);
  286. }
  287. public UniTaskStatus UnsafeGetStatus()
  288. {
  289. return core.UnsafeGetStatus();
  290. }
  291. public void OnCompleted(Action<object> continuation, object state, short token)
  292. {
  293. core.OnCompleted(continuation, state, token);
  294. }
  295. public bool MoveNext()
  296. {
  297. if (completed)
  298. {
  299. TryReturn();
  300. return false;
  301. }
  302. if (cancellationToken.IsCancellationRequested)
  303. {
  304. completed = true;
  305. core.TrySetCanceled(cancellationToken);
  306. return false;
  307. }
  308. if (progress != null && handle.IsValid())
  309. {
  310. progress.Report(handle.PercentComplete);
  311. }
  312. return true;
  313. }
  314. bool TryReturn()
  315. {
  316. TaskTracker.RemoveTracking(this);
  317. core.Reset();
  318. handle = default;
  319. progress = default;
  320. cancellationToken = default;
  321. return pool.TryPush(this);
  322. }
  323. }
  324. #endregion
  325. }
  326. }
  327. #endif