UnityAsyncEventHandler as IUniTaskAsyncEnumerable

master
neuecc 2020-05-12 11:51:50 +09:00
parent 7fc6c6bd36
commit bd6906792d
4 changed files with 292 additions and 16 deletions

View File

@ -43,7 +43,7 @@ namespace NetCoreSandbox
.SelectAwait(x => UniTask.Run(() => x))
.TakeLast(6)
)
{
@ -51,7 +51,7 @@ namespace NetCoreSandbox
Console.WriteLine(item);
}
// AsyncEnumerable.Range(1,10).FirstAsync(
// AsyncEnumerable.Range(1, 10).GroupBy(x=>x).Select(x=>x.first

View File

@ -4,12 +4,14 @@ using System.Threading;
namespace Cysharp.Threading.Tasks.Internal
{
internal interface IPromisePoolItem
// public, allow to user create custom operator with pool.
public interface IPromisePoolItem
{
void Reset();
}
internal class PromisePool<T>
public class PromisePool<T>
where T : class, IPromisePoolItem
{
int count = 0;

View File

@ -162,22 +162,25 @@ namespace Cysharp.Threading.Tasks
}
}
public interface IAsyncClickEventHandler : IDisposable
public interface IAsyncClickEventHandler : IDisposable, IUniTaskAsyncEnumerable<AsyncUnit>
{
UniTask OnClickAsync();
IAsyncClickEventHandler DisableAutoClose();
}
public interface IAsyncValueChangedEventHandler<T> : IDisposable
public interface IAsyncValueChangedEventHandler<T> : IDisposable, IUniTaskAsyncEnumerable<T>
{
UniTask<T> OnValueChangedAsync();
IAsyncValueChangedEventHandler<T> DisableAutoClose();
}
public interface IAsyncEndEditEventHandler<T> : IDisposable
public interface IAsyncEndEditEventHandler<T> : IDisposable, IUniTaskAsyncEnumerable<T>
{
UniTask<T> OnEndEditAsync();
IAsyncEndEditEventHandler<T> DisableAutoClose();
}
public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncClickEventHandler
public class AsyncUnityEventHandler : IUniTaskSource, IDisposable, IAsyncClickEventHandler, IUniTaskAsyncEnumerable<AsyncUnit>
{
static Action<object> cancellationCallback = CancellationCallback;
@ -222,6 +225,7 @@ namespace Cysharp.Threading.Tasks
void Invoke()
{
asyncEnumerator?.SetResult();
core.TrySetResult(AsyncUnit.Default);
}
@ -230,6 +234,13 @@ namespace Cysharp.Threading.Tasks
var self = (AsyncUnityEventHandler)state;
self.Dispose();
// call child cancel
if (self.asyncEnumerator != null)
{
self.asyncEnumerator.CancelFromParent(self.cancellationToken);
self.asyncEnumerator = null;
}
self.core.TrySetCanceled(self.cancellationToken);
}
@ -281,9 +292,118 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
// AsyncEnumerator
bool disableAutoClose;
Enumerator asyncEnumerator;
public AsyncUnityEventHandler DisableAutoClose()
{
disableAutoClose = true;
return this;
}
IAsyncClickEventHandler IAsyncClickEventHandler.DisableAutoClose()
{
disableAutoClose = true;
return this;
}
IUniTaskAsyncEnumerator<AsyncUnit> IUniTaskAsyncEnumerable<AsyncUnit>.GetAsyncEnumerator(CancellationToken cancellationToken)
{
if (this.asyncEnumerator != null)
{
throw new InvalidOperationException("Already acquired GetAsyncEnumerator, does not allow get twice before previous enumerator completed.");
}
this.asyncEnumerator = new Enumerator(this, cancellationToken);
return asyncEnumerator;
}
class Enumerator : Cysharp.Threading.Tasks.Linq.MoveNextSource, IUniTaskAsyncEnumerator<AsyncUnit>
{
static Action<object> cancellationCallback = CancellationCallback;
AsyncUnityEventHandler parent;
CancellationToken cancellationToken;
CancellationTokenRegistration registration;
bool isDisposed;
public Enumerator(AsyncUnityEventHandler parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled && parent.cancellationToken != cancellationToken)
{
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
TaskTracker.TrackActiveTask(this, 3);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeCore(self.cancellationToken);
}
public void CancelFromParent(CancellationToken cancellationToken)
{
// call from parent, avoid parent close.
parent.disableAutoClose = true;
DisposeCore(cancellationToken);
}
public void SetResult()
{
completionSource.TrySetResult(true);
}
public AsyncUnit Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
DisposeCore(CancellationToken.None);
return default;
}
void DisposeCore(CancellationToken cancellationToken)
{
if (!isDisposed)
{
isDisposed = true;
registration.Dispose();
TaskTracker.RemoveTracking(this);
if (!parent.disableAutoClose)
{
parent.Dispose(); // dispose parent.
}
if (parent.asyncEnumerator == this)
{
parent.asyncEnumerator = null;
}
try
{
completionSource.TrySetCanceled(cancellationToken);
}
catch (OperationCanceledException) { }
}
}
}
}
public class AsyncUnityEventHandler<T> : IUniTaskSource<T>, IDisposable, IAsyncValueChangedEventHandler<T>, IAsyncEndEditEventHandler<T>
public class AsyncUnityEventHandler<T> : IUniTaskSource<T>, IDisposable, IAsyncValueChangedEventHandler<T>, IAsyncEndEditEventHandler<T>, IUniTaskAsyncEnumerable<T>
{
static Action<object> cancellationCallback = CancellationCallback;
@ -328,6 +448,7 @@ namespace Cysharp.Threading.Tasks
void Invoke(T result)
{
asyncEnumerator?.SetResult(result);
core.TrySetResult(result);
}
@ -336,6 +457,13 @@ namespace Cysharp.Threading.Tasks
var self = (AsyncUnityEventHandler<T>)state;
self.Dispose();
// call child cancel
if (self.asyncEnumerator != null)
{
self.asyncEnumerator.CancelFromParent(self.cancellationToken);
self.asyncEnumerator = null;
}
self.core.TrySetCanceled(self.cancellationToken);
}
@ -350,6 +478,13 @@ namespace Cysharp.Threading.Tasks
{
unityEvent.RemoveListener(action);
}
asyncEnumerator?.DisposeAsync().Forget();
try
{
core.TrySetCanceled();
}
catch (OperationCanceledException) { }
}
}
@ -397,5 +532,121 @@ namespace Cysharp.Threading.Tasks
{
core.OnCompleted(continuation, state, token);
}
// AsyncEnumerator
bool disableAutoClose;
Enumerator asyncEnumerator;
public AsyncUnityEventHandler<T> DisableAutoClose()
{
disableAutoClose = true;
return this;
}
IAsyncValueChangedEventHandler<T> IAsyncValueChangedEventHandler<T>.DisableAutoClose()
{
disableAutoClose = true;
return this;
}
IAsyncEndEditEventHandler<T> IAsyncEndEditEventHandler<T>.DisableAutoClose()
{
disableAutoClose = true;
return this;
}
IUniTaskAsyncEnumerator<T> IUniTaskAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken cancellationToken)
{
if (this.asyncEnumerator != null)
{
throw new InvalidOperationException("Already acquired GetAsyncEnumerator, does not allow get twice before previous enumerator completed.");
}
this.asyncEnumerator = new Enumerator(this, cancellationToken);
return asyncEnumerator;
}
class Enumerator : Cysharp.Threading.Tasks.Linq.MoveNextSource, IUniTaskAsyncEnumerator<T>
{
static Action<object> cancellationCallback = CancellationCallback;
AsyncUnityEventHandler<T> parent;
CancellationToken cancellationToken;
CancellationTokenRegistration registration;
bool isDisposed;
public Enumerator(AsyncUnityEventHandler<T> parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
if (cancellationToken.CanBeCanceled && parent.cancellationToken != cancellationToken)
{
registration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
TaskTracker.TrackActiveTask(this, 3);
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeCore(self.cancellationToken);
}
public void CancelFromParent(CancellationToken cancellationToken)
{
// call from parent, avoid parent close.
parent.disableAutoClose = true;
DisposeCore(cancellationToken);
}
public void SetResult(T result)
{
Current = result;
completionSource.TrySetResult(true);
}
public T Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
DisposeCore(CancellationToken.None);
return default;
}
void DisposeCore(CancellationToken cancellationToken)
{
if (!isDisposed)
{
isDisposed = true;
registration.Dispose();
TaskTracker.RemoveTracking(this);
if (!parent.disableAutoClose)
{
parent.Dispose(); // dispose parent.
}
if (parent.asyncEnumerator == this)
{
parent.asyncEnumerator = null;
}
try
{
completionSource.TrySetCanceled(cancellationToken);
}
catch (OperationCanceledException) { }
}
}
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using Cysharp.Threading.Tasks.Linq;
using Cysharp.Threading.Tasks.Triggers;
using System.Collections;
using System.Collections.Generic;
@ -12,7 +13,6 @@ using Unity.Jobs;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Cysharp.Threading.Tasks.Linq;
public struct MyJob : IJob
{
@ -105,16 +105,16 @@ public class SandboxMain : MonoBehaviour
}
void Start()
async void Start()
{
Application.SetStackTraceLogType(LogType.Error, StackTraceLogType.Full);
Application.SetStackTraceLogType(LogType.Exception, StackTraceLogType.Full);
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop);
//ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop);
Update2().Forget();
//Update2().Forget();
//RunStandardDelayAsync().Forget();
@ -144,7 +144,6 @@ public class SandboxMain : MonoBehaviour
//StartCoroutine(cor);
Debug.Log(EqualityComparer<MyEnum>.Default.GetType().FullName);
//this.TryGetComponent(
@ -163,12 +162,36 @@ public class SandboxMain : MonoBehaviour
Application.logMessageReceived += Application_logMessageReceived;
UniTask<int> foo = UniTask.FromResult(10);
// foo.Status.IsCanceled
// 5回クリックされるまで待つ、とか。
Debug.Log("Await start.");
await okButton.GetAsyncClickEventHandler().DisableAutoClose()
.Select((_, clickCount) => clickCount + 1)
.FirstAsync(x => x == 5);
Debug.Log("Click 5 times.");
Foo(foo);
//ucs = new UniTaskCompletionSource();