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.
245 lines
6.5 KiB
245 lines
6.5 KiB
#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; |
|
} |
|
} |
|
} |
|
}
|
|
|