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.
313 lines
8.4 KiB
313 lines
8.4 KiB
3 years ago
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.ExceptionServices;
|
||
|
|
||
|
namespace ET
|
||
|
{
|
||
|
[AsyncMethodBuilder(typeof (ETAsyncTaskMethodBuilder))]
|
||
|
public class ETTask: ICriticalNotifyCompletion
|
||
|
{
|
||
|
public static Action<Exception> ExceptionHandler;
|
||
|
|
||
|
public static ETTaskCompleted CompletedTask
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return new ETTaskCompleted();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static readonly Queue<ETTask> queue = new Queue<ETTask>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// 请不要随便使用ETTask的对象池,除非你完全搞懂了ETTask!!!
|
||
|
/// 假如开启了池,await之后不能再操作ETTask,否则可能操作到再次从池中分配出来的ETTask,产生灾难性的后果
|
||
|
/// SetResult的时候请现将tcs置空,避免多次对同一个ETTask SetResult
|
||
|
/// </summary>
|
||
|
public static ETTask Create(bool fromPool = false)
|
||
|
{
|
||
|
if (!fromPool)
|
||
|
{
|
||
|
return new ETTask();
|
||
|
}
|
||
|
|
||
|
if (queue.Count == 0)
|
||
|
{
|
||
|
return new ETTask() {fromPool = true};
|
||
|
}
|
||
|
return queue.Dequeue();
|
||
|
}
|
||
|
|
||
|
private void Recycle()
|
||
|
{
|
||
|
if (!this.fromPool)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.state = AwaiterStatus.Pending;
|
||
|
this.callback = null;
|
||
|
queue.Enqueue(this);
|
||
|
// 太多了,回收一下
|
||
|
if (queue.Count > 1000)
|
||
|
{
|
||
|
queue.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool fromPool;
|
||
|
private AwaiterStatus state;
|
||
|
private object callback; // Action or ExceptionDispatchInfo
|
||
|
|
||
|
private ETTask()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
private async ETVoid InnerCoroutine()
|
||
|
{
|
||
|
await this;
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void Coroutine()
|
||
|
{
|
||
|
InnerCoroutine().Coroutine();
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public ETTask GetAwaiter()
|
||
|
{
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
|
||
|
public bool IsCompleted
|
||
|
{
|
||
|
[DebuggerHidden]
|
||
|
get
|
||
|
{
|
||
|
return this.state != AwaiterStatus.Pending;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void UnsafeOnCompleted(Action action)
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
action?.Invoke();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.callback = action;
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void OnCompleted(Action action)
|
||
|
{
|
||
|
this.UnsafeOnCompleted(action);
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void GetResult()
|
||
|
{
|
||
|
switch (this.state)
|
||
|
{
|
||
|
case AwaiterStatus.Succeeded:
|
||
|
this.Recycle();
|
||
|
break;
|
||
|
case AwaiterStatus.Faulted:
|
||
|
ExceptionDispatchInfo c = this.callback as ExceptionDispatchInfo;
|
||
|
this.callback = null;
|
||
|
this.Recycle();
|
||
|
c?.Throw();
|
||
|
break;
|
||
|
default:
|
||
|
throw new NotSupportedException("ETTask does not allow call GetResult directly when task not completed. Please use 'await'.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void SetResult()
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
|
||
|
}
|
||
|
|
||
|
this.state = AwaiterStatus.Succeeded;
|
||
|
|
||
|
Action c = this.callback as Action;
|
||
|
this.callback = null;
|
||
|
c?.Invoke();
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
[DebuggerHidden]
|
||
|
public void SetException(Exception e)
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
|
||
|
}
|
||
|
|
||
|
this.state = AwaiterStatus.Faulted;
|
||
|
|
||
|
Action c = this.callback as Action;
|
||
|
this.callback = ExceptionDispatchInfo.Capture(e);
|
||
|
c?.Invoke();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[AsyncMethodBuilder(typeof (ETAsyncTaskMethodBuilder<>))]
|
||
|
public class ETTask<T>: ICriticalNotifyCompletion
|
||
|
{
|
||
|
private static readonly Queue<ETTask<T>> queue = new Queue<ETTask<T>>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// 请不要随便使用ETTask的对象池,除非你完全搞懂了ETTask!!!
|
||
|
/// 假如开启了池,await之后不能再操作ETTask,否则可能操作到再次从池中分配出来的ETTask,产生灾难性的后果
|
||
|
/// SetResult的时候请现将tcs置空,避免多次对同一个ETTask SetResult
|
||
|
/// </summary>
|
||
|
public static ETTask<T> Create(bool fromPool = false)
|
||
|
{
|
||
|
if (!fromPool)
|
||
|
{
|
||
|
return new ETTask<T>();
|
||
|
}
|
||
|
|
||
|
if (queue.Count == 0)
|
||
|
{
|
||
|
return new ETTask<T>() { fromPool = true };
|
||
|
}
|
||
|
return queue.Dequeue();
|
||
|
}
|
||
|
|
||
|
private void Recycle()
|
||
|
{
|
||
|
if (!this.fromPool)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
this.callback = null;
|
||
|
this.value = default;
|
||
|
this.state = AwaiterStatus.Pending;
|
||
|
queue.Enqueue(this);
|
||
|
// 太多了,回收一下
|
||
|
if (queue.Count > 1000)
|
||
|
{
|
||
|
queue.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool fromPool;
|
||
|
private AwaiterStatus state;
|
||
|
private T value;
|
||
|
private object callback; // Action or ExceptionDispatchInfo
|
||
|
|
||
|
private ETTask()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
private async ETVoid InnerCoroutine()
|
||
|
{
|
||
|
await this;
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void Coroutine()
|
||
|
{
|
||
|
InnerCoroutine().Coroutine();
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public ETTask<T> GetAwaiter()
|
||
|
{
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public T GetResult()
|
||
|
{
|
||
|
switch (this.state)
|
||
|
{
|
||
|
case AwaiterStatus.Succeeded:
|
||
|
T v = this.value;
|
||
|
this.Recycle();
|
||
|
return v;
|
||
|
case AwaiterStatus.Faulted:
|
||
|
ExceptionDispatchInfo c = this.callback as ExceptionDispatchInfo;
|
||
|
this.callback = null;
|
||
|
this.Recycle();
|
||
|
c?.Throw();
|
||
|
return default;
|
||
|
default:
|
||
|
throw new NotSupportedException("ETask does not allow call GetResult directly when task not completed. Please use 'await'.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public bool IsCompleted
|
||
|
{
|
||
|
[DebuggerHidden]
|
||
|
get
|
||
|
{
|
||
|
return state != AwaiterStatus.Pending;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void UnsafeOnCompleted(Action action)
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
action?.Invoke();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.callback = action;
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void OnCompleted(Action action)
|
||
|
{
|
||
|
this.UnsafeOnCompleted(action);
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void SetResult(T result)
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
|
||
|
}
|
||
|
|
||
|
this.state = AwaiterStatus.Succeeded;
|
||
|
|
||
|
this.value = result;
|
||
|
|
||
|
Action c = this.callback as Action;
|
||
|
this.callback = null;
|
||
|
c?.Invoke();
|
||
|
}
|
||
|
|
||
|
[DebuggerHidden]
|
||
|
public void SetException(Exception e)
|
||
|
{
|
||
|
if (this.state != AwaiterStatus.Pending)
|
||
|
{
|
||
|
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
|
||
|
}
|
||
|
|
||
|
this.state = AwaiterStatus.Faulted;
|
||
|
|
||
|
Action c = this.callback as Action;
|
||
|
this.callback = ExceptionDispatchInfo.Capture(e);
|
||
|
c?.Invoke();
|
||
|
}
|
||
|
}
|
||
|
}
|