diff --git a/Assets/UniRx.Async/AsyncLazy.cs b/Assets/UniRx.Async/AsyncLazy.cs index 1969f61..9a11b2f 100644 --- a/Assets/UniRx.Async/AsyncLazy.cs +++ b/Assets/UniRx.Async/AsyncLazy.cs @@ -6,6 +6,62 @@ using System.Threading; namespace UniRx.Async { + public class AsyncLazy + { + Func valueFactory; + UniTask target; + object syncLock; + bool initialized; + + public AsyncLazy(Func valueFactory) + { + this.valueFactory = valueFactory; + this.target = default; + this.syncLock = new object(); + this.initialized = false; + } + + internal AsyncLazy(UniTask value) + { + this.valueFactory = null; + this.target = value; + this.syncLock = null; + this.initialized = true; + } + + public UniTask Task => EnsureInitialized(); + + public UniTask.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter(); + + UniTask EnsureInitialized() + { + if (Volatile.Read(ref initialized)) + { + return target; + } + + return EnsureInitializedCore(); + } + + UniTask EnsureInitializedCore() + { + lock (syncLock) + { + if (!Volatile.Read(ref initialized)) + { + var f = Interlocked.Exchange(ref valueFactory, null); + if (f != null) + { + target = f().Preserve(); // with preserve(allow multiple await). + Volatile.Write(ref initialized, true); + } + } + } + + return target; + } + } + public class AsyncLazy { Func> valueFactory; diff --git a/Assets/UniRx.Async/UniTask.Factory.cs b/Assets/UniRx.Async/UniTask.Factory.cs index c63d8c3..d0c906a 100644 --- a/Assets/UniRx.Async/UniTask.Factory.cs +++ b/Assets/UniRx.Async/UniTask.Factory.cs @@ -93,6 +93,11 @@ namespace UniRx.Async return factory(); } + public static AsyncLazy Lazy(Func factory) + { + return new AsyncLazy(factory); + } + public static AsyncLazy Lazy(Func> factory) { return new AsyncLazy(factory); diff --git a/Assets/UniRx.Async/UniTaskExtensions.cs b/Assets/UniRx.Async/UniTaskExtensions.cs index 9642af1..795abc1 100644 --- a/Assets/UniRx.Async/UniTaskExtensions.cs +++ b/Assets/UniRx.Async/UniTaskExtensions.cs @@ -180,6 +180,16 @@ namespace UniRx.Async } } + public static AsyncLazy ToAsyncLazy(this UniTask task) + { + return new AsyncLazy(task.Preserve()); // require Preserve + } + + public static AsyncLazy ToAsyncLazy(this UniTask task) + { + return new AsyncLazy(task.Preserve()); // require Preserve + } + public static IEnumerator ToCoroutine(this UniTask task, Action resultHandler = null, Action exceptionHandler = null) { return new ToCoroutineEnumerator(task, resultHandler, exceptionHandler);