You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

483 line
17 KiB

  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.AddressableAssets;
  9. using UnityEngine.ResourceManagement.AsyncOperations;
  10. namespace Cysharp.Threading.Tasks
  11. {
  12. public static class AddressablesAsyncExtensions
  13. {
  14. #region AsyncOperationHandle
  15. public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle)
  16. {
  17. return ToUniTask(handle).GetAwaiter();
  18. }
  19. public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
  20. {
  21. return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
  22. }
  23. public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
  24. {
  25. if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken);
  26. if (!handle.IsValid())
  27. {
  28. // autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed.
  29. return UniTask.CompletedTask;
  30. }
  31. if (handle.IsDone)
  32. {
  33. if (handle.Status == AsyncOperationStatus.Failed)
  34. {
  35. return UniTask.FromException(handle.OperationException);
  36. }
  37. return UniTask.CompletedTask;
  38. }
  39. return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
  40. }
  41. public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
  42. {
  43. AsyncOperationHandle handle;
  44. Action<AsyncOperationHandle> continuationAction;
  45. public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
  46. {
  47. this.handle = handle;
  48. this.continuationAction = null;
  49. }
  50. public bool IsCompleted => handle.IsDone;
  51. public void GetResult()
  52. {
  53. if (continuationAction != null)
  54. {
  55. handle.Completed -= continuationAction;
  56. continuationAction = null;
  57. }
  58. if (handle.Status == AsyncOperationStatus.Failed)
  59. {
  60. var e = handle.OperationException;
  61. handle = default;
  62. ExceptionDispatchInfo.Capture(e).Throw();
  63. }
  64. var result = handle.Result;
  65. handle = default;
  66. }
  67. public void OnCompleted(Action continuation)
  68. {
  69. UnsafeOnCompleted(continuation);
  70. }
  71. public void UnsafeOnCompleted(Action continuation)
  72. {
  73. Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
  74. continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation);
  75. handle.Completed += continuationAction;
  76. }
  77. }
  78. sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource>
  79. {
  80. static TaskPool<AsyncOperationHandleConfiguredSource> pool;
  81. AsyncOperationHandleConfiguredSource nextNode;
  82. public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode;
  83. static AsyncOperationHandleConfiguredSource()
  84. {
  85. TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size);
  86. }
  87. readonly Action<AsyncOperationHandle> completedCallback;
  88. AsyncOperationHandle handle;
  89. CancellationToken cancellationToken;
  90. CancellationTokenRegistration cancellationTokenRegistration;
  91. IProgress<float> progress;
  92. bool autoReleaseWhenCanceled;
  93. bool cancelImmediately;
  94. bool completed;
  95. UniTaskCompletionSourceCore<AsyncUnit> core;
  96. AsyncOperationHandleConfiguredSource()
  97. {
  98. completedCallback = HandleCompleted;
  99. }
  100. public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
  101. {
  102. if (cancellationToken.IsCancellationRequested)
  103. {
  104. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  105. }
  106. if (!pool.TryPop(out var result))
  107. {
  108. result = new AsyncOperationHandleConfiguredSource();
  109. }
  110. result.handle = handle;
  111. result.progress = progress;
  112. result.cancellationToken = cancellationToken;
  113. result.cancelImmediately = cancelImmediately;
  114. result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
  115. result.completed = false;
  116. if (cancelImmediately && cancellationToken.CanBeCanceled)
  117. {
  118. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  119. {
  120. var promise = (AsyncOperationHandleConfiguredSource)state;
  121. if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
  122. {
  123. Addressables.Release(promise.handle);
  124. }
  125. promise.core.TrySetCanceled(promise.cancellationToken);
  126. }, result);
  127. }
  128. TaskTracker.TrackActiveTask(result, 3);
  129. PlayerLoopHelper.AddAction(timing, result);
  130. handle.Completed += result.completedCallback;
  131. token = result.core.Version;
  132. return result;
  133. }
  134. void HandleCompleted(AsyncOperationHandle _)
  135. {
  136. if (handle.IsValid())
  137. {
  138. handle.Completed -= completedCallback;
  139. }
  140. if (completed)
  141. {
  142. return;
  143. }
  144. completed = true;
  145. if (cancellationToken.IsCancellationRequested)
  146. {
  147. if (autoReleaseWhenCanceled && handle.IsValid())
  148. {
  149. Addressables.Release(handle);
  150. }
  151. core.TrySetCanceled(cancellationToken);
  152. }
  153. else if (handle.Status == AsyncOperationStatus.Failed)
  154. {
  155. core.TrySetException(handle.OperationException);
  156. }
  157. else
  158. {
  159. core.TrySetResult(AsyncUnit.Default);
  160. }
  161. }
  162. public void GetResult(short token)
  163. {
  164. try
  165. {
  166. core.GetResult(token);
  167. }
  168. finally
  169. {
  170. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  171. {
  172. TryReturn();
  173. }
  174. else
  175. {
  176. TaskTracker.RemoveTracking(this);
  177. }
  178. }
  179. }
  180. public UniTaskStatus GetStatus(short token)
  181. {
  182. return core.GetStatus(token);
  183. }
  184. public UniTaskStatus UnsafeGetStatus()
  185. {
  186. return core.UnsafeGetStatus();
  187. }
  188. public void OnCompleted(Action<object> continuation, object state, short token)
  189. {
  190. core.OnCompleted(continuation, state, token);
  191. }
  192. public bool MoveNext()
  193. {
  194. if (completed)
  195. {
  196. return false;
  197. }
  198. if (cancellationToken.IsCancellationRequested)
  199. {
  200. completed = true;
  201. if (autoReleaseWhenCanceled && handle.IsValid())
  202. {
  203. Addressables.Release(handle);
  204. }
  205. core.TrySetCanceled(cancellationToken);
  206. return false;
  207. }
  208. if (progress != null && handle.IsValid())
  209. {
  210. progress.Report(handle.GetDownloadStatus().Percent);
  211. }
  212. return true;
  213. }
  214. bool TryReturn()
  215. {
  216. TaskTracker.RemoveTracking(this);
  217. core.Reset();
  218. handle = default;
  219. progress = default;
  220. cancellationToken = default;
  221. cancellationTokenRegistration.Dispose();
  222. return pool.TryPush(this);
  223. }
  224. }
  225. #endregion
  226. #region AsyncOperationHandle_T
  227. public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)
  228. {
  229. return ToUniTask(handle).GetAwaiter();
  230. }
  231. public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
  232. {
  233. return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);
  234. }
  235. public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)
  236. {
  237. if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);
  238. if (!handle.IsValid())
  239. {
  240. throw new Exception("Attempting to use an invalid operation handle");
  241. }
  242. if (handle.IsDone)
  243. {
  244. if (handle.Status == AsyncOperationStatus.Failed)
  245. {
  246. return UniTask.FromException<T>(handle.OperationException);
  247. }
  248. return UniTask.FromResult(handle.Result);
  249. }
  250. return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);
  251. }
  252. sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>
  253. {
  254. static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;
  255. AsyncOperationHandleConfiguredSource<T> nextNode;
  256. public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode;
  257. static AsyncOperationHandleConfiguredSource()
  258. {
  259. TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size);
  260. }
  261. readonly Action<AsyncOperationHandle<T>> completedCallback;
  262. AsyncOperationHandle<T> handle;
  263. CancellationToken cancellationToken;
  264. CancellationTokenRegistration cancellationTokenRegistration;
  265. IProgress<float> progress;
  266. bool autoReleaseWhenCanceled;
  267. bool cancelImmediately;
  268. bool completed;
  269. UniTaskCompletionSourceCore<T> core;
  270. AsyncOperationHandleConfiguredSource()
  271. {
  272. completedCallback = HandleCompleted;
  273. }
  274. public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token)
  275. {
  276. if (cancellationToken.IsCancellationRequested)
  277. {
  278. return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
  279. }
  280. if (!pool.TryPop(out var result))
  281. {
  282. result = new AsyncOperationHandleConfiguredSource<T>();
  283. }
  284. result.handle = handle;
  285. result.cancellationToken = cancellationToken;
  286. result.completed = false;
  287. result.progress = progress;
  288. result.autoReleaseWhenCanceled = autoReleaseWhenCanceled;
  289. result.cancelImmediately = cancelImmediately;
  290. if (cancelImmediately && cancellationToken.CanBeCanceled)
  291. {
  292. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  293. {
  294. var promise = (AsyncOperationHandleConfiguredSource<T>)state;
  295. if (promise.autoReleaseWhenCanceled && promise.handle.IsValid())
  296. {
  297. Addressables.Release(promise.handle);
  298. }
  299. promise.core.TrySetCanceled(promise.cancellationToken);
  300. }, result);
  301. }
  302. TaskTracker.TrackActiveTask(result, 3);
  303. PlayerLoopHelper.AddAction(timing, result);
  304. handle.Completed += result.completedCallback;
  305. token = result.core.Version;
  306. return result;
  307. }
  308. void HandleCompleted(AsyncOperationHandle<T> argHandle)
  309. {
  310. if (handle.IsValid())
  311. {
  312. handle.Completed -= completedCallback;
  313. }
  314. if (completed)
  315. {
  316. return;
  317. }
  318. completed = true;
  319. if (cancellationToken.IsCancellationRequested)
  320. {
  321. if (autoReleaseWhenCanceled && handle.IsValid())
  322. {
  323. Addressables.Release(handle);
  324. }
  325. core.TrySetCanceled(cancellationToken);
  326. }
  327. else if (argHandle.Status == AsyncOperationStatus.Failed)
  328. {
  329. core.TrySetException(argHandle.OperationException);
  330. }
  331. else
  332. {
  333. core.TrySetResult(argHandle.Result);
  334. }
  335. }
  336. public T GetResult(short token)
  337. {
  338. try
  339. {
  340. return core.GetResult(token);
  341. }
  342. finally
  343. {
  344. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  345. {
  346. TryReturn();
  347. }
  348. else
  349. {
  350. TaskTracker.RemoveTracking(this);
  351. }
  352. }
  353. }
  354. void IUniTaskSource.GetResult(short token)
  355. {
  356. GetResult(token);
  357. }
  358. public UniTaskStatus GetStatus(short token)
  359. {
  360. return core.GetStatus(token);
  361. }
  362. public UniTaskStatus UnsafeGetStatus()
  363. {
  364. return core.UnsafeGetStatus();
  365. }
  366. public void OnCompleted(Action<object> continuation, object state, short token)
  367. {
  368. core.OnCompleted(continuation, state, token);
  369. }
  370. public bool MoveNext()
  371. {
  372. if (completed)
  373. {
  374. return false;
  375. }
  376. if (cancellationToken.IsCancellationRequested)
  377. {
  378. completed = true;
  379. if (autoReleaseWhenCanceled && handle.IsValid())
  380. {
  381. Addressables.Release(handle);
  382. }
  383. core.TrySetCanceled(cancellationToken);
  384. return false;
  385. }
  386. if (progress != null && handle.IsValid())
  387. {
  388. progress.Report(handle.GetDownloadStatus().Percent);
  389. }
  390. return true;
  391. }
  392. bool TryReturn()
  393. {
  394. TaskTracker.RemoveTracking(this);
  395. core.Reset();
  396. handle = default;
  397. progress = default;
  398. cancellationToken = default;
  399. cancellationTokenRegistration.Dispose();
  400. return pool.TryPush(this);
  401. }
  402. }
  403. #endregion
  404. }
  405. }
  406. #endif