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.
 
 
 

957 lines
33 KiB

  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.Tracing;
  5. using System.Threading;
  6. using Cysharp.Threading.Tasks.Internal;
  7. namespace Cysharp.Threading.Tasks
  8. {
  9. public partial struct UniTask
  10. {
  11. public static UniTask WaitUntil(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  12. {
  13. return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
  14. }
  15. public static UniTask WaitUntil<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  16. {
  17. return new UniTask(WaitUntilPromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
  18. }
  19. public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  20. {
  21. return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
  22. }
  23. public static UniTask WaitWhile<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  24. {
  25. return new UniTask(WaitWhilePromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
  26. }
  27. public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update, bool completeImmediately = false)
  28. {
  29. return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, completeImmediately, out var token), token);
  30. }
  31. public static UniTask<U> WaitUntilValueChanged<T, U>(T target, Func<T, U> monitorFunction, PlayerLoopTiming monitorTiming = PlayerLoopTiming.Update, IEqualityComparer<U> equalityComparer = null, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
  32. where T : class
  33. {
  34. var unityObject = target as UnityEngine.Object;
  35. var isUnityObject = target is UnityEngine.Object; // don't use (unityObject == null)
  36. return new UniTask<U>(isUnityObject
  37. ? WaitUntilValueChangedUnityObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out var token)
  38. : WaitUntilValueChangedStandardObjectPromise<T, U>.Create(target, monitorFunction, equalityComparer, monitorTiming, cancellationToken, cancelImmediately, out token), token);
  39. }
  40. sealed class WaitUntilPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise>
  41. {
  42. static TaskPool<WaitUntilPromise> pool;
  43. WaitUntilPromise nextNode;
  44. public ref WaitUntilPromise NextNode => ref nextNode;
  45. static WaitUntilPromise()
  46. {
  47. TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise), () => pool.Size);
  48. }
  49. Func<bool> predicate;
  50. CancellationToken cancellationToken;
  51. CancellationTokenRegistration cancellationTokenRegistration;
  52. bool cancelImmediately;
  53. UniTaskCompletionSourceCore<object> core;
  54. WaitUntilPromise()
  55. {
  56. }
  57. public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  58. {
  59. if (cancellationToken.IsCancellationRequested)
  60. {
  61. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  62. }
  63. if (!pool.TryPop(out var result))
  64. {
  65. result = new WaitUntilPromise();
  66. }
  67. result.predicate = predicate;
  68. result.cancellationToken = cancellationToken;
  69. result.cancelImmediately = cancelImmediately;
  70. if (cancelImmediately && cancellationToken.CanBeCanceled)
  71. {
  72. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  73. {
  74. var promise = (WaitUntilPromise)state;
  75. promise.core.TrySetCanceled(promise.cancellationToken);
  76. }, result);
  77. }
  78. TaskTracker.TrackActiveTask(result, 3);
  79. PlayerLoopHelper.AddAction(timing, result);
  80. token = result.core.Version;
  81. return result;
  82. }
  83. public void GetResult(short token)
  84. {
  85. try
  86. {
  87. core.GetResult(token);
  88. }
  89. finally
  90. {
  91. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  92. {
  93. TryReturn();
  94. }
  95. else
  96. {
  97. TaskTracker.RemoveTracking(this);
  98. }
  99. }
  100. }
  101. public UniTaskStatus GetStatus(short token)
  102. {
  103. return core.GetStatus(token);
  104. }
  105. public UniTaskStatus UnsafeGetStatus()
  106. {
  107. return core.UnsafeGetStatus();
  108. }
  109. public void OnCompleted(Action<object> continuation, object state, short token)
  110. {
  111. core.OnCompleted(continuation, state, token);
  112. }
  113. public bool MoveNext()
  114. {
  115. if (cancellationToken.IsCancellationRequested)
  116. {
  117. core.TrySetCanceled(cancellationToken);
  118. return false;
  119. }
  120. try
  121. {
  122. if (!predicate())
  123. {
  124. return true;
  125. }
  126. }
  127. catch (Exception ex)
  128. {
  129. core.TrySetException(ex);
  130. return false;
  131. }
  132. core.TrySetResult(null);
  133. return false;
  134. }
  135. bool TryReturn()
  136. {
  137. TaskTracker.RemoveTracking(this);
  138. core.Reset();
  139. predicate = default;
  140. cancellationToken = default;
  141. cancellationTokenRegistration.Dispose();
  142. cancelImmediately = default;
  143. return pool.TryPush(this);
  144. }
  145. }
  146. sealed class WaitUntilPromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise<T>>
  147. {
  148. static TaskPool<WaitUntilPromise<T>> pool;
  149. WaitUntilPromise<T> nextNode;
  150. public ref WaitUntilPromise<T> NextNode => ref nextNode;
  151. static WaitUntilPromise()
  152. {
  153. TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise<T>), () => pool.Size);
  154. }
  155. Func<T, bool> predicate;
  156. T argument;
  157. CancellationToken cancellationToken;
  158. CancellationTokenRegistration cancellationTokenRegistration;
  159. bool cancelImmediately;
  160. UniTaskCompletionSourceCore<object> core;
  161. WaitUntilPromise()
  162. {
  163. }
  164. public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  165. {
  166. if (cancellationToken.IsCancellationRequested)
  167. {
  168. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  169. }
  170. if (!pool.TryPop(out var result))
  171. {
  172. result = new WaitUntilPromise<T>();
  173. }
  174. result.predicate = predicate;
  175. result.argument = argument;
  176. result.cancellationToken = cancellationToken;
  177. result.cancelImmediately = cancelImmediately;
  178. if (cancelImmediately && cancellationToken.CanBeCanceled)
  179. {
  180. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  181. {
  182. var promise = (WaitUntilPromise<T>)state;
  183. promise.core.TrySetCanceled(promise.cancellationToken);
  184. }, result);
  185. }
  186. TaskTracker.TrackActiveTask(result, 3);
  187. PlayerLoopHelper.AddAction(timing, result);
  188. token = result.core.Version;
  189. return result;
  190. }
  191. public void GetResult(short token)
  192. {
  193. try
  194. {
  195. core.GetResult(token);
  196. }
  197. finally
  198. {
  199. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  200. {
  201. TryReturn();
  202. }
  203. else
  204. {
  205. TaskTracker.RemoveTracking(this);
  206. }
  207. }
  208. }
  209. public UniTaskStatus GetStatus(short token)
  210. {
  211. return core.GetStatus(token);
  212. }
  213. public UniTaskStatus UnsafeGetStatus()
  214. {
  215. return core.UnsafeGetStatus();
  216. }
  217. public void OnCompleted(Action<object> continuation, object state, short token)
  218. {
  219. core.OnCompleted(continuation, state, token);
  220. }
  221. public bool MoveNext()
  222. {
  223. if (cancellationToken.IsCancellationRequested)
  224. {
  225. core.TrySetCanceled(cancellationToken);
  226. return false;
  227. }
  228. try
  229. {
  230. if (!predicate(argument))
  231. {
  232. return true;
  233. }
  234. }
  235. catch (Exception ex)
  236. {
  237. core.TrySetException(ex);
  238. return false;
  239. }
  240. core.TrySetResult(null);
  241. return false;
  242. }
  243. bool TryReturn()
  244. {
  245. TaskTracker.RemoveTracking(this);
  246. core.Reset();
  247. predicate = default;
  248. argument = default;
  249. cancellationToken = default;
  250. cancellationTokenRegistration.Dispose();
  251. cancelImmediately = default;
  252. return pool.TryPush(this);
  253. }
  254. }
  255. sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
  256. {
  257. static TaskPool<WaitWhilePromise> pool;
  258. WaitWhilePromise nextNode;
  259. public ref WaitWhilePromise NextNode => ref nextNode;
  260. static WaitWhilePromise()
  261. {
  262. TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise), () => pool.Size);
  263. }
  264. Func<bool> predicate;
  265. CancellationToken cancellationToken;
  266. CancellationTokenRegistration cancellationTokenRegistration;
  267. bool cancelImmediately;
  268. UniTaskCompletionSourceCore<object> core;
  269. WaitWhilePromise()
  270. {
  271. }
  272. public static IUniTaskSource Create(Func<bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  273. {
  274. if (cancellationToken.IsCancellationRequested)
  275. {
  276. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  277. }
  278. if (!pool.TryPop(out var result))
  279. {
  280. result = new WaitWhilePromise();
  281. }
  282. result.predicate = predicate;
  283. result.cancellationToken = cancellationToken;
  284. result.cancelImmediately = cancelImmediately;
  285. if (cancelImmediately && cancellationToken.CanBeCanceled)
  286. {
  287. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  288. {
  289. var promise = (WaitWhilePromise)state;
  290. promise.core.TrySetCanceled(promise.cancellationToken);
  291. }, result);
  292. }
  293. TaskTracker.TrackActiveTask(result, 3);
  294. PlayerLoopHelper.AddAction(timing, result);
  295. token = result.core.Version;
  296. return result;
  297. }
  298. public void GetResult(short token)
  299. {
  300. try
  301. {
  302. core.GetResult(token);
  303. }
  304. finally
  305. {
  306. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  307. {
  308. TryReturn();
  309. }
  310. else
  311. {
  312. TaskTracker.RemoveTracking(this);
  313. }
  314. }
  315. }
  316. public UniTaskStatus GetStatus(short token)
  317. {
  318. return core.GetStatus(token);
  319. }
  320. public UniTaskStatus UnsafeGetStatus()
  321. {
  322. return core.UnsafeGetStatus();
  323. }
  324. public void OnCompleted(Action<object> continuation, object state, short token)
  325. {
  326. core.OnCompleted(continuation, state, token);
  327. }
  328. public bool MoveNext()
  329. {
  330. if (cancellationToken.IsCancellationRequested)
  331. {
  332. core.TrySetCanceled(cancellationToken);
  333. return false;
  334. }
  335. try
  336. {
  337. if (predicate())
  338. {
  339. return true;
  340. }
  341. }
  342. catch (Exception ex)
  343. {
  344. core.TrySetException(ex);
  345. return false;
  346. }
  347. core.TrySetResult(null);
  348. return false;
  349. }
  350. bool TryReturn()
  351. {
  352. TaskTracker.RemoveTracking(this);
  353. core.Reset();
  354. predicate = default;
  355. cancellationToken = default;
  356. cancellationTokenRegistration.Dispose();
  357. cancelImmediately = default;
  358. return pool.TryPush(this);
  359. }
  360. }
  361. sealed class WaitWhilePromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise<T>>
  362. {
  363. static TaskPool<WaitWhilePromise<T>> pool;
  364. WaitWhilePromise<T> nextNode;
  365. public ref WaitWhilePromise<T> NextNode => ref nextNode;
  366. static WaitWhilePromise()
  367. {
  368. TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise<T>), () => pool.Size);
  369. }
  370. Func<T, bool> predicate;
  371. T argument;
  372. CancellationToken cancellationToken;
  373. CancellationTokenRegistration cancellationTokenRegistration;
  374. bool cancelImmediately;
  375. UniTaskCompletionSourceCore<object> core;
  376. WaitWhilePromise()
  377. {
  378. }
  379. public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  380. {
  381. if (cancellationToken.IsCancellationRequested)
  382. {
  383. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  384. }
  385. if (!pool.TryPop(out var result))
  386. {
  387. result = new WaitWhilePromise<T>();
  388. }
  389. result.predicate = predicate;
  390. result.argument = argument;
  391. result.cancellationToken = cancellationToken;
  392. result.cancelImmediately = cancelImmediately;
  393. if (cancelImmediately && cancellationToken.CanBeCanceled)
  394. {
  395. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  396. {
  397. var promise = (WaitWhilePromise<T>)state;
  398. promise.core.TrySetCanceled(promise.cancellationToken);
  399. }, result);
  400. }
  401. TaskTracker.TrackActiveTask(result, 3);
  402. PlayerLoopHelper.AddAction(timing, result);
  403. token = result.core.Version;
  404. return result;
  405. }
  406. public void GetResult(short token)
  407. {
  408. try
  409. {
  410. core.GetResult(token);
  411. }
  412. finally
  413. {
  414. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  415. {
  416. TryReturn();
  417. }
  418. else
  419. {
  420. TaskTracker.RemoveTracking(this);
  421. }
  422. }
  423. }
  424. public UniTaskStatus GetStatus(short token)
  425. {
  426. return core.GetStatus(token);
  427. }
  428. public UniTaskStatus UnsafeGetStatus()
  429. {
  430. return core.UnsafeGetStatus();
  431. }
  432. public void OnCompleted(Action<object> continuation, object state, short token)
  433. {
  434. core.OnCompleted(continuation, state, token);
  435. }
  436. public bool MoveNext()
  437. {
  438. if (cancellationToken.IsCancellationRequested)
  439. {
  440. core.TrySetCanceled(cancellationToken);
  441. return false;
  442. }
  443. try
  444. {
  445. if (predicate(argument))
  446. {
  447. return true;
  448. }
  449. }
  450. catch (Exception ex)
  451. {
  452. core.TrySetException(ex);
  453. return false;
  454. }
  455. core.TrySetResult(null);
  456. return false;
  457. }
  458. bool TryReturn()
  459. {
  460. TaskTracker.RemoveTracking(this);
  461. core.Reset();
  462. predicate = default;
  463. argument = default;
  464. cancellationToken = default;
  465. cancellationTokenRegistration.Dispose();
  466. cancelImmediately = default;
  467. return pool.TryPush(this);
  468. }
  469. }
  470. sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
  471. {
  472. static TaskPool<WaitUntilCanceledPromise> pool;
  473. WaitUntilCanceledPromise nextNode;
  474. public ref WaitUntilCanceledPromise NextNode => ref nextNode;
  475. static WaitUntilCanceledPromise()
  476. {
  477. TaskPool.RegisterSizeGetter(typeof(WaitUntilCanceledPromise), () => pool.Size);
  478. }
  479. CancellationToken cancellationToken;
  480. CancellationTokenRegistration cancellationTokenRegistration;
  481. bool cancelImmediately;
  482. UniTaskCompletionSourceCore<object> core;
  483. WaitUntilCanceledPromise()
  484. {
  485. }
  486. public static IUniTaskSource Create(CancellationToken cancellationToken, PlayerLoopTiming timing, bool cancelImmediately, out short token)
  487. {
  488. if (cancellationToken.IsCancellationRequested)
  489. {
  490. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  491. }
  492. if (!pool.TryPop(out var result))
  493. {
  494. result = new WaitUntilCanceledPromise();
  495. }
  496. result.cancellationToken = cancellationToken;
  497. result.cancelImmediately = cancelImmediately;
  498. if (cancelImmediately && cancellationToken.CanBeCanceled)
  499. {
  500. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  501. {
  502. var promise = (WaitUntilCanceledPromise)state;
  503. promise.core.TrySetResult(null);
  504. }, result);
  505. }
  506. TaskTracker.TrackActiveTask(result, 3);
  507. PlayerLoopHelper.AddAction(timing, result);
  508. token = result.core.Version;
  509. return result;
  510. }
  511. public void GetResult(short token)
  512. {
  513. try
  514. {
  515. core.GetResult(token);
  516. }
  517. finally
  518. {
  519. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  520. {
  521. TryReturn();
  522. }
  523. else
  524. {
  525. TaskTracker.RemoveTracking(this);
  526. }
  527. }
  528. }
  529. public UniTaskStatus GetStatus(short token)
  530. {
  531. return core.GetStatus(token);
  532. }
  533. public UniTaskStatus UnsafeGetStatus()
  534. {
  535. return core.UnsafeGetStatus();
  536. }
  537. public void OnCompleted(Action<object> continuation, object state, short token)
  538. {
  539. core.OnCompleted(continuation, state, token);
  540. }
  541. public bool MoveNext()
  542. {
  543. if (cancellationToken.IsCancellationRequested)
  544. {
  545. core.TrySetResult(null);
  546. return false;
  547. }
  548. return true;
  549. }
  550. bool TryReturn()
  551. {
  552. TaskTracker.RemoveTracking(this);
  553. core.Reset();
  554. cancellationToken = default;
  555. cancellationTokenRegistration.Dispose();
  556. cancelImmediately = default;
  557. return pool.TryPush(this);
  558. }
  559. }
  560. // where T : UnityEngine.Object, can not add constraint
  561. sealed class WaitUntilValueChangedUnityObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedUnityObjectPromise<T, U>>
  562. {
  563. static TaskPool<WaitUntilValueChangedUnityObjectPromise<T, U>> pool;
  564. WaitUntilValueChangedUnityObjectPromise<T, U> nextNode;
  565. public ref WaitUntilValueChangedUnityObjectPromise<T, U> NextNode => ref nextNode;
  566. static WaitUntilValueChangedUnityObjectPromise()
  567. {
  568. TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedUnityObjectPromise<T, U>), () => pool.Size);
  569. }
  570. T target;
  571. UnityEngine.Object targetAsUnityObject;
  572. U currentValue;
  573. Func<T, U> monitorFunction;
  574. IEqualityComparer<U> equalityComparer;
  575. CancellationToken cancellationToken;
  576. CancellationTokenRegistration cancellationTokenRegistration;
  577. bool cancelImmediately;
  578. UniTaskCompletionSourceCore<U> core;
  579. WaitUntilValueChangedUnityObjectPromise()
  580. {
  581. }
  582. public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  583. {
  584. if (cancellationToken.IsCancellationRequested)
  585. {
  586. return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token);
  587. }
  588. if (!pool.TryPop(out var result))
  589. {
  590. result = new WaitUntilValueChangedUnityObjectPromise<T, U>();
  591. }
  592. result.target = target;
  593. result.targetAsUnityObject = target as UnityEngine.Object;
  594. result.monitorFunction = monitorFunction;
  595. result.currentValue = monitorFunction(target);
  596. result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
  597. result.cancellationToken = cancellationToken;
  598. result.cancelImmediately = cancelImmediately;
  599. if (cancelImmediately && cancellationToken.CanBeCanceled)
  600. {
  601. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  602. {
  603. var promise = (WaitUntilValueChangedUnityObjectPromise<T, U>)state;
  604. promise.core.TrySetCanceled(promise.cancellationToken);
  605. }, result);
  606. }
  607. TaskTracker.TrackActiveTask(result, 3);
  608. PlayerLoopHelper.AddAction(timing, result);
  609. token = result.core.Version;
  610. return result;
  611. }
  612. public U GetResult(short token)
  613. {
  614. try
  615. {
  616. return core.GetResult(token);
  617. }
  618. finally
  619. {
  620. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  621. {
  622. TryReturn();
  623. }
  624. else
  625. {
  626. TaskTracker.RemoveTracking(this);
  627. }
  628. }
  629. }
  630. void IUniTaskSource.GetResult(short token)
  631. {
  632. GetResult(token);
  633. }
  634. public UniTaskStatus GetStatus(short token)
  635. {
  636. return core.GetStatus(token);
  637. }
  638. public UniTaskStatus UnsafeGetStatus()
  639. {
  640. return core.UnsafeGetStatus();
  641. }
  642. public void OnCompleted(Action<object> continuation, object state, short token)
  643. {
  644. core.OnCompleted(continuation, state, token);
  645. }
  646. public bool MoveNext()
  647. {
  648. if (cancellationToken.IsCancellationRequested || targetAsUnityObject == null) // destroyed = cancel.
  649. {
  650. core.TrySetCanceled(cancellationToken);
  651. return false;
  652. }
  653. U nextValue = default(U);
  654. try
  655. {
  656. nextValue = monitorFunction(target);
  657. if (equalityComparer.Equals(currentValue, nextValue))
  658. {
  659. return true;
  660. }
  661. }
  662. catch (Exception ex)
  663. {
  664. core.TrySetException(ex);
  665. return false;
  666. }
  667. core.TrySetResult(nextValue);
  668. return false;
  669. }
  670. bool TryReturn()
  671. {
  672. TaskTracker.RemoveTracking(this);
  673. core.Reset();
  674. target = default;
  675. currentValue = default;
  676. monitorFunction = default;
  677. equalityComparer = default;
  678. cancellationToken = default;
  679. cancellationTokenRegistration.Dispose();
  680. cancelImmediately = default;
  681. return pool.TryPush(this);
  682. }
  683. }
  684. sealed class WaitUntilValueChangedStandardObjectPromise<T, U> : IUniTaskSource<U>, IPlayerLoopItem, ITaskPoolNode<WaitUntilValueChangedStandardObjectPromise<T, U>>
  685. where T : class
  686. {
  687. static TaskPool<WaitUntilValueChangedStandardObjectPromise<T, U>> pool;
  688. WaitUntilValueChangedStandardObjectPromise<T, U> nextNode;
  689. public ref WaitUntilValueChangedStandardObjectPromise<T, U> NextNode => ref nextNode;
  690. static WaitUntilValueChangedStandardObjectPromise()
  691. {
  692. TaskPool.RegisterSizeGetter(typeof(WaitUntilValueChangedStandardObjectPromise<T, U>), () => pool.Size);
  693. }
  694. WeakReference<T> target;
  695. U currentValue;
  696. Func<T, U> monitorFunction;
  697. IEqualityComparer<U> equalityComparer;
  698. CancellationToken cancellationToken;
  699. CancellationTokenRegistration cancellationTokenRegistration;
  700. bool cancelImmediately;
  701. UniTaskCompletionSourceCore<U> core;
  702. WaitUntilValueChangedStandardObjectPromise()
  703. {
  704. }
  705. public static IUniTaskSource<U> Create(T target, Func<T, U> monitorFunction, IEqualityComparer<U> equalityComparer, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
  706. {
  707. if (cancellationToken.IsCancellationRequested)
  708. {
  709. return AutoResetUniTaskCompletionSource<U>.CreateFromCanceled(cancellationToken, out token);
  710. }
  711. if (!pool.TryPop(out var result))
  712. {
  713. result = new WaitUntilValueChangedStandardObjectPromise<T, U>();
  714. }
  715. result.target = new WeakReference<T>(target, false); // wrap in WeakReference.
  716. result.monitorFunction = monitorFunction;
  717. result.currentValue = monitorFunction(target);
  718. result.equalityComparer = equalityComparer ?? UnityEqualityComparer.GetDefault<U>();
  719. result.cancellationToken = cancellationToken;
  720. result.cancelImmediately = cancelImmediately;
  721. if (cancelImmediately && cancellationToken.CanBeCanceled)
  722. {
  723. result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
  724. {
  725. var promise = (WaitUntilValueChangedStandardObjectPromise<T, U>)state;
  726. promise.core.TrySetCanceled(promise.cancellationToken);
  727. }, result);
  728. }
  729. TaskTracker.TrackActiveTask(result, 3);
  730. PlayerLoopHelper.AddAction(timing, result);
  731. token = result.core.Version;
  732. return result;
  733. }
  734. public U GetResult(short token)
  735. {
  736. try
  737. {
  738. return core.GetResult(token);
  739. }
  740. finally
  741. {
  742. if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
  743. {
  744. TryReturn();
  745. }
  746. else
  747. {
  748. TaskTracker.RemoveTracking(this);
  749. }
  750. }
  751. }
  752. void IUniTaskSource.GetResult(short token)
  753. {
  754. GetResult(token);
  755. }
  756. public UniTaskStatus GetStatus(short token)
  757. {
  758. return core.GetStatus(token);
  759. }
  760. public UniTaskStatus UnsafeGetStatus()
  761. {
  762. return core.UnsafeGetStatus();
  763. }
  764. public void OnCompleted(Action<object> continuation, object state, short token)
  765. {
  766. core.OnCompleted(continuation, state, token);
  767. }
  768. public bool MoveNext()
  769. {
  770. if (cancellationToken.IsCancellationRequested || !target.TryGetTarget(out var t)) // doesn't find = cancel.
  771. {
  772. core.TrySetCanceled(cancellationToken);
  773. return false;
  774. }
  775. U nextValue = default(U);
  776. try
  777. {
  778. nextValue = monitorFunction(t);
  779. if (equalityComparer.Equals(currentValue, nextValue))
  780. {
  781. return true;
  782. }
  783. }
  784. catch (Exception ex)
  785. {
  786. core.TrySetException(ex);
  787. return false;
  788. }
  789. core.TrySetResult(nextValue);
  790. return false;
  791. }
  792. bool TryReturn()
  793. {
  794. TaskTracker.RemoveTracking(this);
  795. core.Reset();
  796. target = default;
  797. currentValue = default;
  798. monitorFunction = default;
  799. equalityComparer = default;
  800. cancellationToken = default;
  801. cancellationTokenRegistration.Dispose();
  802. cancelImmediately = default;
  803. return pool.TryPush(this);
  804. }
  805. }
  806. }
  807. }