UnityAsyncExtensions.tt 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <#@ template debug="false" hostspecific="false" language="C#" #>
  2. <#@ assembly name="System.Core" #>
  3. <#@ import namespace="System.Linq" #>
  4. <#@ import namespace="System.Text" #>
  5. <#@ import namespace="System.Collections.Generic" #>
  6. <#@ output extension=".cs" #>
  7. <#
  8. var types = new (string typeName, string returnType, string returnField)[]
  9. {
  10. ("AsyncOperation", "void", null),
  11. ("ResourceRequest", "UnityEngine.Object", "asset"),
  12. ("AssetBundleRequest", "UnityEngine.Object", "asset"), // allAssets?
  13. ("AssetBundleCreateRequest", "AssetBundle", "assetBundle"),
  14. ("UnityWebRequestAsyncOperation", "UnityWebRequest", "webRequest") // -> #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
  15. };
  16. Func<string, string> ToUniTaskReturnType = x => (x == "void") ? "UniTask" : $"UniTask<{x}>";
  17. Func<string, string> ToIUniTaskSourceReturnType = x => (x == "void") ? "IUniTaskSource" : $"IUniTaskSource<{x}>";
  18. Func<(string typeName, string returnType, string returnField), bool> IsUnityWebRequest = x => x.returnType == "UnityWebRequest";
  19. Func<(string typeName, string returnType, string returnField), bool> IsAssetBundleModule = x => x.typeName == "AssetBundleRequest" || x.typeName == "AssetBundleCreateRequest";
  20. Func<(string typeName, string returnType, string returnField), bool> IsVoid = x => x.returnType == "void";
  21. #>
  22. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  23. using System;
  24. using System.Runtime.CompilerServices;
  25. using System.Threading;
  26. using UnityEngine;
  27. using Cysharp.Threading.Tasks.Internal;
  28. #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
  29. using UnityEngine.Networking;
  30. #endif
  31. namespace Cysharp.Threading.Tasks
  32. {
  33. public static partial class UnityAsyncExtensions
  34. {
  35. <# foreach(var t in types) { #>
  36. <# if(IsUnityWebRequest(t)) { #>
  37. #if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT)
  38. <# } else if(IsAssetBundleModule(t)) { #>
  39. #if UNITASK_ASSETBUNDLE_SUPPORT
  40. <# } #>
  41. #region <#= t.typeName #>
  42. public static <#= t.typeName #>Awaiter GetAwaiter(this <#= t.typeName #> asyncOperation)
  43. {
  44. Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
  45. return new <#= t.typeName #>Awaiter(asyncOperation);
  46. }
  47. public static <#= ToUniTaskReturnType(t.returnType) #> WithCancellation(this <#= t.typeName #> asyncOperation, CancellationToken cancellationToken)
  48. {
  49. return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
  50. }
  51. public static <#= ToUniTaskReturnType(t.returnType) #> ToUniTask(this <#= t.typeName #> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken))
  52. {
  53. Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
  54. if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<#= IsVoid(t) ? "" : "<" + t.returnType + ">" #>(cancellationToken);
  55. <# if(IsUnityWebRequest(t)) { #>
  56. if (asyncOperation.isDone)
  57. {
  58. if (asyncOperation.webRequest.IsError())
  59. {
  60. return UniTask.FromException<UnityWebRequest>(new UnityWebRequestException(asyncOperation.webRequest));
  61. }
  62. return UniTask.FromResult(asyncOperation.webRequest);
  63. }
  64. <# } else { #>
  65. if (asyncOperation.isDone) return <#= IsVoid(t) ? "UniTask.CompletedTask" : $"UniTask.FromResult(asyncOperation.{t.returnField})" #>;
  66. <# } #>
  67. return new <#= ToUniTaskReturnType(t.returnType) #>(<#= t.typeName #>ConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, out var token), token);
  68. }
  69. public struct <#= t.typeName #>Awaiter : ICriticalNotifyCompletion
  70. {
  71. <#= t.typeName #> asyncOperation;
  72. Action<AsyncOperation> continuationAction;
  73. public <#= t.typeName #>Awaiter(<#= t.typeName #> asyncOperation)
  74. {
  75. this.asyncOperation = asyncOperation;
  76. this.continuationAction = null;
  77. }
  78. public bool IsCompleted => asyncOperation.isDone;
  79. public <#= t.returnType #> GetResult()
  80. {
  81. if (continuationAction != null)
  82. {
  83. asyncOperation.completed -= continuationAction;
  84. continuationAction = null;
  85. <# if (!IsVoid(t)) { #>
  86. var result = <#= $"asyncOperation.{t.returnField}" #>;
  87. asyncOperation = null;
  88. <# if(IsUnityWebRequest(t)) { #>
  89. if (result.IsError())
  90. {
  91. throw new UnityWebRequestException(result);
  92. }
  93. <# } #>
  94. return result;
  95. <# } else { #>
  96. asyncOperation = null;
  97. <# } #>
  98. }
  99. else
  100. {
  101. <# if (!IsVoid(t)) { #>
  102. var result = <#= $"asyncOperation.{t.returnField}" #>;
  103. asyncOperation = null;
  104. <# if(IsUnityWebRequest(t)) { #>
  105. if (result.IsError())
  106. {
  107. throw new UnityWebRequestException(result);
  108. }
  109. <# } #>
  110. return result;
  111. <# } else { #>
  112. asyncOperation = null;
  113. <# } #>
  114. }
  115. }
  116. public void OnCompleted(Action continuation)
  117. {
  118. UnsafeOnCompleted(continuation);
  119. }
  120. public void UnsafeOnCompleted(Action continuation)
  121. {
  122. Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
  123. continuationAction = PooledDelegate<AsyncOperation>.Create(continuation);
  124. asyncOperation.completed += continuationAction;
  125. }
  126. }
  127. sealed class <#= t.typeName #>ConfiguredSource : <#= ToIUniTaskSourceReturnType(t.returnType) #>, IPlayerLoopItem, ITaskPoolNode<<#= t.typeName #>ConfiguredSource>
  128. {
  129. static TaskPool<<#= t.typeName #>ConfiguredSource> pool;
  130. <#= t.typeName #>ConfiguredSource nextNode;
  131. public ref <#= t.typeName #>ConfiguredSource NextNode => ref nextNode;
  132. static <#= t.typeName #>ConfiguredSource()
  133. {
  134. TaskPool.RegisterSizeGetter(typeof(<#= t.typeName #>ConfiguredSource), () => pool.Size);
  135. }
  136. <#= t.typeName #> asyncOperation;
  137. IProgress<float> progress;
  138. CancellationToken cancellationToken;
  139. UniTaskCompletionSourceCore<<#= IsVoid(t) ? "AsyncUnit" : t.returnType #>> core;
  140. <#= t.typeName #>ConfiguredSource()
  141. {
  142. }
  143. public static <#= ToIUniTaskSourceReturnType(t.returnType) #> Create(<#= t.typeName #> asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
  144. {
  145. if (cancellationToken.IsCancellationRequested)
  146. {
  147. return AutoResetUniTaskCompletionSource<#= IsVoid(t) ? "" : $"<{t.returnType}>" #>.CreateFromCanceled(cancellationToken, out token);
  148. }
  149. if (!pool.TryPop(out var result))
  150. {
  151. result = new <#= t.typeName #>ConfiguredSource();
  152. }
  153. result.asyncOperation = asyncOperation;
  154. result.progress = progress;
  155. result.cancellationToken = cancellationToken;
  156. TaskTracker.TrackActiveTask(result, 3);
  157. PlayerLoopHelper.AddAction(timing, result);
  158. token = result.core.Version;
  159. return result;
  160. }
  161. public <#= t.returnType #> GetResult(short token)
  162. {
  163. try
  164. {
  165. <# if (!IsVoid(t)) { #>
  166. return core.GetResult(token);
  167. <# } else { #>
  168. core.GetResult(token);
  169. <# } #>
  170. }
  171. finally
  172. {
  173. TryReturn();
  174. }
  175. }
  176. <# if (!IsVoid(t)) { #>
  177. void IUniTaskSource.GetResult(short token)
  178. {
  179. GetResult(token);
  180. }
  181. <# } #>
  182. public UniTaskStatus GetStatus(short token)
  183. {
  184. return core.GetStatus(token);
  185. }
  186. public UniTaskStatus UnsafeGetStatus()
  187. {
  188. return core.UnsafeGetStatus();
  189. }
  190. public void OnCompleted(Action<object> continuation, object state, short token)
  191. {
  192. core.OnCompleted(continuation, state, token);
  193. }
  194. public bool MoveNext()
  195. {
  196. if (cancellationToken.IsCancellationRequested)
  197. {
  198. <# if(IsUnityWebRequest(t)) { #>
  199. asyncOperation.webRequest.Abort();
  200. <# } #>
  201. core.TrySetCanceled(cancellationToken);
  202. return false;
  203. }
  204. if (progress != null)
  205. {
  206. progress.Report(asyncOperation.progress);
  207. }
  208. if (asyncOperation.isDone)
  209. {
  210. <# if(IsUnityWebRequest(t)) { #>
  211. if (asyncOperation.webRequest.IsError())
  212. {
  213. core.TrySetException(new UnityWebRequestException(asyncOperation.webRequest));
  214. }
  215. else
  216. {
  217. core.TrySetResult(asyncOperation.webRequest);
  218. }
  219. <# } else { #>
  220. core.TrySetResult(<#= IsVoid(t) ? "AsyncUnit.Default" : $"asyncOperation.{t.returnField}" #>);
  221. <# } #>
  222. return false;
  223. }
  224. return true;
  225. }
  226. bool TryReturn()
  227. {
  228. TaskTracker.RemoveTracking(this);
  229. core.Reset();
  230. asyncOperation = default;
  231. progress = default;
  232. cancellationToken = default;
  233. return pool.TryPush(this);
  234. }
  235. }
  236. #endregion
  237. <# if(IsUnityWebRequest(t) || IsAssetBundleModule(t)) { #>
  238. #endif
  239. <# } #>
  240. <# } #>
  241. }
  242. }