You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
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;
|
|
}
|
|
}
|
|
}
|
|
}
|