246 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
| #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
 | |
| 
 | |
| using System;
 | |
| using System.Threading;
 | |
| 
 | |
| namespace Cysharp.Threading.Tasks
 | |
| {
 | |
|     public class AsyncLazy
 | |
|     {
 | |
|         static Action<object> continuation = SetCompletionSource;
 | |
| 
 | |
|         Func<UniTask> taskFactory;
 | |
|         UniTaskCompletionSource completionSource;
 | |
|         UniTask.Awaiter awaiter;
 | |
| 
 | |
|         object syncLock;
 | |
|         bool initialized;
 | |
| 
 | |
|         public AsyncLazy(Func<UniTask> taskFactory)
 | |
|         {
 | |
|             this.taskFactory = taskFactory;
 | |
|             this.completionSource = new UniTaskCompletionSource();
 | |
|             this.syncLock = new object();
 | |
|             this.initialized = false;
 | |
|         }
 | |
| 
 | |
|         internal AsyncLazy(UniTask task)
 | |
|         {
 | |
|             this.taskFactory = null;
 | |
|             this.completionSource = new UniTaskCompletionSource();
 | |
|             this.syncLock = null;
 | |
|             this.initialized = true;
 | |
| 
 | |
|             var awaiter = task.GetAwaiter();
 | |
|             if (awaiter.IsCompleted)
 | |
|             {
 | |
|                 SetCompletionSource(awaiter);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 this.awaiter = awaiter;
 | |
|                 awaiter.SourceOnCompleted(continuation, this);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public UniTask Task
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 EnsureInitialized();
 | |
|                 return completionSource.Task;
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
 | |
| 
 | |
|         void EnsureInitialized()
 | |
|         {
 | |
|             if (Volatile.Read(ref initialized))
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             EnsureInitializedCore();
 | |
|         }
 | |
| 
 | |
|         void EnsureInitializedCore()
 | |
|         {
 | |
|             lock (syncLock)
 | |
|             {
 | |
|                 if (!Volatile.Read(ref initialized))
 | |
|                 {
 | |
|                     var f = Interlocked.Exchange(ref taskFactory, null);
 | |
|                     if (f != null)
 | |
|                     {
 | |
|                         var task = f();
 | |
|                         var awaiter = task.GetAwaiter();
 | |
|                         if (awaiter.IsCompleted)
 | |
|                         {
 | |
|                             SetCompletionSource(awaiter);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             this.awaiter = awaiter;
 | |
|                             awaiter.SourceOnCompleted(continuation, this);
 | |
|                         }
 | |
| 
 | |
|                         Volatile.Write(ref initialized, true);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void SetCompletionSource(in UniTask.Awaiter awaiter)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 awaiter.GetResult();
 | |
|                 completionSource.TrySetResult();
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 completionSource.TrySetException(ex);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static void SetCompletionSource(object state)
 | |
|         {
 | |
|             var self = (AsyncLazy)state;
 | |
|             try
 | |
|             {
 | |
|                 self.awaiter.GetResult();
 | |
|                 self.completionSource.TrySetResult();
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 self.completionSource.TrySetException(ex);
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 self.awaiter = default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public class AsyncLazy<T>
 | |
|     {
 | |
|         static Action<object> continuation = SetCompletionSource;
 | |
| 
 | |
|         Func<UniTask<T>> taskFactory;
 | |
|         UniTaskCompletionSource<T> completionSource;
 | |
|         UniTask<T>.Awaiter awaiter;
 | |
| 
 | |
|         object syncLock;
 | |
|         bool initialized;
 | |
| 
 | |
|         public AsyncLazy(Func<UniTask<T>> taskFactory)
 | |
|         {
 | |
|             this.taskFactory = taskFactory;
 | |
|             this.completionSource = new UniTaskCompletionSource<T>();
 | |
|             this.syncLock = new object();
 | |
|             this.initialized = false;
 | |
|         }
 | |
| 
 | |
|         internal AsyncLazy(UniTask<T> task)
 | |
|         {
 | |
|             this.taskFactory = null;
 | |
|             this.completionSource = new UniTaskCompletionSource<T>();
 | |
|             this.syncLock = null;
 | |
|             this.initialized = true;
 | |
| 
 | |
|             var awaiter = task.GetAwaiter();
 | |
|             if (awaiter.IsCompleted)
 | |
|             {
 | |
|                 SetCompletionSource(awaiter);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 this.awaiter = awaiter;
 | |
|                 awaiter.SourceOnCompleted(continuation, this);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public UniTask<T> Task
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 EnsureInitialized();
 | |
|                 return completionSource.Task;
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
 | |
| 
 | |
|         void EnsureInitialized()
 | |
|         {
 | |
|             if (Volatile.Read(ref initialized))
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             EnsureInitializedCore();
 | |
|         }
 | |
| 
 | |
|         void EnsureInitializedCore()
 | |
|         {
 | |
|             lock (syncLock)
 | |
|             {
 | |
|                 if (!Volatile.Read(ref initialized))
 | |
|                 {
 | |
|                     var f = Interlocked.Exchange(ref taskFactory, null);
 | |
|                     if (f != null)
 | |
|                     {
 | |
|                         var task = f();
 | |
|                         var awaiter = task.GetAwaiter();
 | |
|                         if (awaiter.IsCompleted)
 | |
|                         {
 | |
|                             SetCompletionSource(awaiter);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             this.awaiter = awaiter;
 | |
|                             awaiter.SourceOnCompleted(continuation, this);
 | |
|                         }
 | |
| 
 | |
|                         Volatile.Write(ref initialized, true);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 var result = awaiter.GetResult();
 | |
|                 completionSource.TrySetResult(result);
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 completionSource.TrySetException(ex);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static void SetCompletionSource(object state)
 | |
|         {
 | |
|             var self = (AsyncLazy<T>)state;
 | |
|             try
 | |
|             {
 | |
|                 var result = self.awaiter.GetResult();
 | |
|                 self.completionSource.TrySetResult(result);
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 self.completionSource.TrySetException(ex);
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 self.awaiter = default;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |