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

@ -51,7 +51,7 @@ namespace NetCoreSandbox
Console.WriteLine(item); Console.WriteLine(item);
} }
// AsyncEnumerable.Range(1,10).FirstAsync(
// AsyncEnumerable.Range(1, 10).GroupBy(x=>x).Select(x=>x.first // 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 namespace Cysharp.Threading.Tasks.Internal
{ {
internal interface IPromisePoolItem // public, allow to user create custom operator with pool.
public interface IPromisePoolItem
{ {
void Reset(); void Reset();
} }
internal class PromisePool<T> public class PromisePool<T>
where T : class, IPromisePoolItem where T : class, IPromisePoolItem
{ {
int count = 0; 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(); UniTask OnClickAsync();
IAsyncClickEventHandler DisableAutoClose();
} }
public interface IAsyncValueChangedEventHandler<T> : IDisposable public interface IAsyncValueChangedEventHandler<T> : IDisposable, IUniTaskAsyncEnumerable<T>
{ {
UniTask<T> OnValueChangedAsync(); UniTask<T> OnValueChangedAsync();
IAsyncValueChangedEventHandler<T> DisableAutoClose();
} }
public interface IAsyncEndEditEventHandler<T> : IDisposable public interface IAsyncEndEditEventHandler<T> : IDisposable, IUniTaskAsyncEnumerable<T>
{ {
UniTask<T> OnEndEditAsync(); 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; static Action<object> cancellationCallback = CancellationCallback;
@ -222,6 +225,7 @@ namespace Cysharp.Threading.Tasks
void Invoke() void Invoke()
{ {
asyncEnumerator?.SetResult();
core.TrySetResult(AsyncUnit.Default); core.TrySetResult(AsyncUnit.Default);
} }
@ -230,6 +234,13 @@ namespace Cysharp.Threading.Tasks
var self = (AsyncUnityEventHandler)state; var self = (AsyncUnityEventHandler)state;
self.Dispose(); self.Dispose();
// call child cancel
if (self.asyncEnumerator != null)
{
self.asyncEnumerator.CancelFromParent(self.cancellationToken);
self.asyncEnumerator = null;
}
self.core.TrySetCanceled(self.cancellationToken); self.core.TrySetCanceled(self.cancellationToken);
} }
@ -281,9 +292,118 @@ namespace Cysharp.Threading.Tasks
{ {
core.OnCompleted(continuation, state, token); core.OnCompleted(continuation, state, token);
} }
// AsyncEnumerator
bool disableAutoClose;
Enumerator asyncEnumerator;
public AsyncUnityEventHandler DisableAutoClose()
{
disableAutoClose = true;
return this;
} }
public class AsyncUnityEventHandler<T> : IUniTaskSource<T>, IDisposable, IAsyncValueChangedEventHandler<T>, IAsyncEndEditEventHandler<T> 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>, IUniTaskAsyncEnumerable<T>
{ {
static Action<object> cancellationCallback = CancellationCallback; static Action<object> cancellationCallback = CancellationCallback;
@ -328,6 +448,7 @@ namespace Cysharp.Threading.Tasks
void Invoke(T result) void Invoke(T result)
{ {
asyncEnumerator?.SetResult(result);
core.TrySetResult(result); core.TrySetResult(result);
} }
@ -336,6 +457,13 @@ namespace Cysharp.Threading.Tasks
var self = (AsyncUnityEventHandler<T>)state; var self = (AsyncUnityEventHandler<T>)state;
self.Dispose(); self.Dispose();
// call child cancel
if (self.asyncEnumerator != null)
{
self.asyncEnumerator.CancelFromParent(self.cancellationToken);
self.asyncEnumerator = null;
}
self.core.TrySetCanceled(self.cancellationToken); self.core.TrySetCanceled(self.cancellationToken);
} }
@ -350,6 +478,13 @@ namespace Cysharp.Threading.Tasks
{ {
unityEvent.RemoveListener(action); unityEvent.RemoveListener(action);
} }
asyncEnumerator?.DisposeAsync().Forget();
try
{
core.TrySetCanceled();
}
catch (OperationCanceledException) { }
} }
} }
@ -397,5 +532,121 @@ namespace Cysharp.Threading.Tasks
{ {
core.OnCompleted(continuation, state, token); 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 System;
using Cysharp.Threading.Tasks.Linq;
using Cysharp.Threading.Tasks.Triggers; using Cysharp.Threading.Tasks.Triggers;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -12,7 +13,6 @@ using Unity.Jobs;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine.UI; using UnityEngine.UI;
using Cysharp.Threading.Tasks.Linq;
public struct MyJob : IJob 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.Error, StackTraceLogType.Full);
Application.SetStackTraceLogType(LogType.Exception, StackTraceLogType.Full); Application.SetStackTraceLogType(LogType.Exception, StackTraceLogType.Full);
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop(); var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop); //ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop);
Update2().Forget(); //Update2().Forget();
//RunStandardDelayAsync().Forget(); //RunStandardDelayAsync().Forget();
@ -144,7 +144,6 @@ public class SandboxMain : MonoBehaviour
//StartCoroutine(cor); //StartCoroutine(cor);
Debug.Log(EqualityComparer<MyEnum>.Default.GetType().FullName);
//this.TryGetComponent( //this.TryGetComponent(
@ -163,12 +162,36 @@ public class SandboxMain : MonoBehaviour
Application.logMessageReceived += Application_logMessageReceived; Application.logMessageReceived += Application_logMessageReceived;
UniTask<int> foo = UniTask.FromResult(10);
// foo.Status.IsCanceled // 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(); //ucs = new UniTaskCompletionSource();