mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-07-31 01:40:42 +00:00
Permissions, RateControl and Auditing
This commit is contained in:
@@ -1,52 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
public class AsyncAwaiter : INotifyCompletion
|
||||
{
|
||||
Action callback = null;
|
||||
|
||||
AsyncException exception = null;
|
||||
|
||||
object result;
|
||||
private static readonly Action CompletedSentinel = () => { };
|
||||
|
||||
private Action continuation;
|
||||
private AsyncException exception;
|
||||
private object result;
|
||||
private readonly AsyncReply reply;
|
||||
|
||||
public AsyncAwaiter(AsyncReply reply)
|
||||
{
|
||||
reply.Then(x =>
|
||||
{
|
||||
this.IsCompleted = true;
|
||||
this.result = x;
|
||||
this.callback?.Invoke();
|
||||
}).Error(x =>
|
||||
{
|
||||
exception = x;
|
||||
this.IsCompleted = true;
|
||||
this.callback?.Invoke();
|
||||
});
|
||||
this.reply = reply;
|
||||
reply.Then(Complete).Error(Fail);
|
||||
}
|
||||
|
||||
public object GetResult()
|
||||
{
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
throw reply.GetExceptionForAwait();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsCompleted { get; private set; }
|
||||
public bool IsCompleted
|
||||
=> ReferenceEquals(Volatile.Read(ref continuation), CompletedSentinel);
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (IsCompleted)
|
||||
continuation?.Invoke();
|
||||
else
|
||||
// Continue....
|
||||
callback = continuation;
|
||||
if (continuation == null)
|
||||
throw new ArgumentNullException(nameof(continuation));
|
||||
|
||||
var previous = Interlocked.CompareExchange(ref this.continuation, continuation, null);
|
||||
if (ReferenceEquals(previous, CompletedSentinel))
|
||||
{
|
||||
continuation();
|
||||
}
|
||||
else if (previous != null)
|
||||
{
|
||||
throw new InvalidOperationException("The awaiter already has a continuation.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Complete(object value)
|
||||
{
|
||||
result = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void Fail(AsyncException value)
|
||||
{
|
||||
exception = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void InvokeContinuation()
|
||||
{
|
||||
var registeredContinuation = Interlocked.Exchange(ref continuation, CompletedSentinel);
|
||||
if (registeredContinuation != null && !ReferenceEquals(registeredContinuation, CompletedSentinel))
|
||||
registeredContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
public class AsyncAwaiter<T> : INotifyCompletion
|
||||
{
|
||||
Action callback = null;
|
||||
private static readonly Action CompletedSentinel = () => { };
|
||||
|
||||
AsyncException exception = null;
|
||||
|
||||
T result;
|
||||
private Action continuation;
|
||||
private AsyncException exception;
|
||||
private T result;
|
||||
private readonly AsyncReply<T> reply;
|
||||
|
||||
public AsyncAwaiter(AsyncReply<T> reply)
|
||||
{
|
||||
reply.Then(x =>
|
||||
{
|
||||
this.IsCompleted = true;
|
||||
this.result = (T)x;
|
||||
this.callback?.Invoke();
|
||||
}).Error(x =>
|
||||
{
|
||||
exception = x;
|
||||
this.IsCompleted = true;
|
||||
this.callback?.Invoke();
|
||||
});
|
||||
this.reply = reply;
|
||||
reply.Then(Complete).Error(Fail);
|
||||
}
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
throw reply.GetExceptionForAwait();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsCompleted { get; private set; }
|
||||
public bool IsCompleted
|
||||
=> ReferenceEquals(Volatile.Read(ref continuation), CompletedSentinel);
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (IsCompleted)
|
||||
continuation?.Invoke();
|
||||
else
|
||||
// Continue....
|
||||
callback = continuation;
|
||||
if (continuation == null)
|
||||
throw new ArgumentNullException(nameof(continuation));
|
||||
|
||||
var previous = Interlocked.CompareExchange(ref this.continuation, continuation, null);
|
||||
if (ReferenceEquals(previous, CompletedSentinel))
|
||||
{
|
||||
continuation();
|
||||
}
|
||||
else if (previous != null)
|
||||
{
|
||||
throw new InvalidOperationException("The awaiter already has a continuation.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Complete(T value)
|
||||
{
|
||||
result = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void Fail(AsyncException value)
|
||||
{
|
||||
exception = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void InvokeContinuation()
|
||||
{
|
||||
var registeredContinuation = Interlocked.Exchange(ref continuation, CompletedSentinel);
|
||||
if (registeredContinuation != null && !ReferenceEquals(registeredContinuation, CompletedSentinel))
|
||||
registeredContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Core;
|
||||
@@ -44,6 +45,7 @@ public class AsyncBag<T> : AsyncReply, IAsyncBag
|
||||
|
||||
int count = 0;
|
||||
bool sealedBag = false;
|
||||
readonly object bagLock = new object();
|
||||
|
||||
|
||||
public virtual Type ArrayType { get; set; } = typeof(T);
|
||||
@@ -79,24 +81,28 @@ public class AsyncBag<T> : AsyncReply, IAsyncBag
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
if (sealedBag)
|
||||
return;
|
||||
object[] pending;
|
||||
lock (bagLock)
|
||||
{
|
||||
if (sealedBag)
|
||||
return;
|
||||
|
||||
sealedBag = true;
|
||||
sealedBag = true;
|
||||
pending = replies.ToArray();
|
||||
}
|
||||
|
||||
var results = ArrayType == null ? new T[replies.Count]
|
||||
: Array.CreateInstance(ArrayType, replies.Count);
|
||||
var results = ArrayType == null ? new T[pending.Length]
|
||||
: Array.CreateInstance(ArrayType, pending.Length);
|
||||
|
||||
if (replies.Count == 0)
|
||||
if (pending.Length == 0)
|
||||
{
|
||||
Trigger(results);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < replies.Count; i++)
|
||||
for (var i = 0; i < pending.Length; i++)
|
||||
{
|
||||
|
||||
var k = replies[i];
|
||||
var k = pending[i];
|
||||
var index = i;
|
||||
|
||||
if (k is AsyncReply reply)
|
||||
@@ -104,19 +110,17 @@ public class AsyncBag<T> : AsyncReply, IAsyncBag
|
||||
reply.Then((r) =>
|
||||
{
|
||||
results.SetValue(r, index);
|
||||
count++;
|
||||
if (count == replies.Count)
|
||||
if (Interlocked.Increment(ref count) == pending.Length)
|
||||
Trigger(results);
|
||||
}).Error(e => TriggerError(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ArrayType != null)
|
||||
replies[i] = RuntimeCaster.Cast(replies[i], ArrayType);
|
||||
|
||||
results.SetValue(replies[i], index);
|
||||
count++;
|
||||
if (count == replies.Count)
|
||||
k = RuntimeCaster.Cast(k, ArrayType);
|
||||
|
||||
results.SetValue(k, index);
|
||||
if (Interlocked.Increment(ref count) == pending.Length)
|
||||
Trigger(results);
|
||||
}
|
||||
}
|
||||
@@ -125,20 +129,24 @@ public class AsyncBag<T> : AsyncReply, IAsyncBag
|
||||
|
||||
public void Add(object valueOrReply)
|
||||
{
|
||||
if (!sealedBag)
|
||||
lock (bagLock)
|
||||
{
|
||||
//if (valueOrReply is AsyncReply)
|
||||
//{
|
||||
// results.Add(default(T));
|
||||
replies.Add(valueOrReply);
|
||||
//}
|
||||
if (!sealedBag)
|
||||
replies.Add(valueOrReply);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void AddBag(AsyncBag<T> bag)
|
||||
{
|
||||
foreach (var r in bag.replies)
|
||||
if (bag == null)
|
||||
throw new ArgumentNullException(nameof(bag));
|
||||
|
||||
object[] source;
|
||||
lock (bag.bagLock)
|
||||
source = bag.replies.ToArray();
|
||||
|
||||
foreach (var r in source)
|
||||
Add(r);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
public class AsyncBagAwaiter<T> : INotifyCompletion
|
||||
{
|
||||
Action callback = null;
|
||||
private static readonly Action CompletedSentinel = () => { };
|
||||
|
||||
AsyncException exception = null;
|
||||
|
||||
T[] result;
|
||||
private Action continuation;
|
||||
private AsyncException exception;
|
||||
private T[] result;
|
||||
|
||||
public AsyncBagAwaiter(AsyncBag<T> reply)
|
||||
{
|
||||
reply.Then(x =>
|
||||
{
|
||||
this.IsCompleted = true;
|
||||
this.result = x;
|
||||
this.callback?.Invoke();
|
||||
}).Error(x =>
|
||||
{
|
||||
exception = x;
|
||||
this.IsCompleted = true;
|
||||
this.callback?.Invoke();
|
||||
});
|
||||
reply.Then(Complete).Error(Fail);
|
||||
}
|
||||
|
||||
public T[] GetResult()
|
||||
{
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsCompleted { get; private set; }
|
||||
public bool IsCompleted
|
||||
=> ReferenceEquals(Volatile.Read(ref continuation), CompletedSentinel);
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (IsCompleted)
|
||||
continuation?.Invoke();
|
||||
else
|
||||
// Continue....
|
||||
callback = continuation;
|
||||
if (continuation == null)
|
||||
throw new ArgumentNullException(nameof(continuation));
|
||||
|
||||
var previous = Interlocked.CompareExchange(ref this.continuation, continuation, null);
|
||||
if (ReferenceEquals(previous, CompletedSentinel))
|
||||
{
|
||||
continuation();
|
||||
}
|
||||
else if (previous != null)
|
||||
{
|
||||
throw new InvalidOperationException("The awaiter already has a continuation.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Complete(T[] value)
|
||||
{
|
||||
result = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void Fail(AsyncException value)
|
||||
{
|
||||
exception = value;
|
||||
InvokeContinuation();
|
||||
}
|
||||
|
||||
private void InvokeContinuation()
|
||||
{
|
||||
var registeredContinuation = Interlocked.Exchange(ref continuation, CompletedSentinel);
|
||||
if (registeredContinuation != null && !ReferenceEquals(registeredContinuation, CompletedSentinel))
|
||||
registeredContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,21 +108,22 @@ public class AsyncQueue<T> : AsyncReply<T>
|
||||
Arrival = DateTime.Now,
|
||||
HasResource = hasResource
|
||||
});
|
||||
resultReady = false;
|
||||
}
|
||||
|
||||
resultReady = false;
|
||||
if (reply.Ready)
|
||||
processQueue(default(T));
|
||||
else
|
||||
reply.Then(processQueue);
|
||||
reply.Then(processQueue).Error(TriggerError);
|
||||
}
|
||||
|
||||
public void Remove(AsyncReply<T> reply)
|
||||
{
|
||||
lock (queueLock)
|
||||
{
|
||||
var item = list.FirstOrDefault(i => i.Reply == reply);
|
||||
list.Remove(item);
|
||||
var index = list.FindIndex(item => ReferenceEquals(item.Reply, reply));
|
||||
if (index >= 0)
|
||||
list.RemoveAt(index);
|
||||
}
|
||||
|
||||
processQueue(default(T));
|
||||
@@ -145,34 +146,34 @@ public class AsyncQueue<T> : AsyncReply<T>
|
||||
}
|
||||
}
|
||||
|
||||
var flushId = currentFlushId++;
|
||||
if (batchSize > 0)
|
||||
{
|
||||
var flushId = currentFlushId++;
|
||||
var readyItems = list.GetRange(0, batchSize);
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
if (list[i].Reply.Ready)
|
||||
// Shift the remaining list once. The previous RemoveAt(0) loop shifted
|
||||
// it once per delivered item and became quadratic for large batches.
|
||||
list.RemoveRange(0, batchSize);
|
||||
|
||||
foreach (var item in readyItems)
|
||||
{
|
||||
Trigger(list[i].Reply.Result);
|
||||
Trigger(item.Reply.Result);
|
||||
resultReady = false;
|
||||
|
||||
if (captureProcessedItems)
|
||||
{
|
||||
var p = list[i];
|
||||
p.Delivered = DateTime.Now;
|
||||
p.Ready = p.Reply.ReadyTime;
|
||||
p.BatchSize = batchSize;
|
||||
p.FlushId = flushId;
|
||||
//p.HasResource = p.Reply. (p.Ready - p.Arrival).TotalMilliseconds > 5;
|
||||
processed.Add(p);
|
||||
var processedItem = item;
|
||||
processedItem.Delivered = DateTime.Now;
|
||||
processedItem.Ready = processedItem.Reply.ReadyTime;
|
||||
processedItem.BatchSize = batchSize;
|
||||
processedItem.FlushId = flushId;
|
||||
processed.Add(processedItem);
|
||||
}
|
||||
|
||||
list.RemoveAt(i);
|
||||
|
||||
i--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
resultReady = (list.Count == 0);
|
||||
resultReady = list.Count == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncQueue()
|
||||
|
||||
+225
-265
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
@@ -22,408 +22,368 @@ SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Resource;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
[AsyncMethodBuilder(typeof(AsyncReplyBuilder))]
|
||||
public class AsyncReply
|
||||
{
|
||||
|
||||
public DateTime ReadyTime;
|
||||
|
||||
protected List<Action<object>> callbacks = new List<Action<object>>();
|
||||
protected object result;
|
||||
// These lists are intentionally lazy. Completed replies are common and should not
|
||||
// allocate callback storage or a kernel-backed wait primitive unless it is needed.
|
||||
protected List<Action<object>> callbacks;
|
||||
protected List<Action<AsyncException>> errorCallbacks;
|
||||
protected List<Action<ProgressType, uint, uint>> progressCallbacks;
|
||||
protected List<Action<object>> chunkCallbacks;
|
||||
protected List<Action<object>> propagationCallbacks;
|
||||
protected List<Action<byte, string>> warningCallbacks;
|
||||
|
||||
protected List<Action<AsyncException>> errorCallbacks = null;
|
||||
protected volatile object result;
|
||||
protected volatile bool resultReady;
|
||||
|
||||
protected List<Action<ProgressType, uint, uint>> progressCallbacks = null;
|
||||
|
||||
protected List<Action<object>> chunkCallbacks = null;
|
||||
|
||||
protected List<Action<object>> propagationCallbacks = null;
|
||||
protected List<Action<byte, string>> warningCallbacks = null;
|
||||
|
||||
|
||||
object asyncLock = new object();
|
||||
|
||||
//public Timer timeout;// = new Timer()
|
||||
protected bool resultReady = false;
|
||||
AsyncException exception;
|
||||
// StackTrace trace;
|
||||
AutoResetEvent mutex = new AutoResetEvent(false);
|
||||
private readonly object asyncLock = new object();
|
||||
private volatile AsyncException exception;
|
||||
private Exception observedException;
|
||||
private ManualResetEventSlim completionEvent;
|
||||
|
||||
public static int MaxId;
|
||||
|
||||
public int Id;
|
||||
|
||||
public bool Ready
|
||||
{
|
||||
get { return resultReady; }
|
||||
}
|
||||
protected string codePath, codeMethod;
|
||||
protected int codeLine;
|
||||
|
||||
public bool Ready => resultReady;
|
||||
|
||||
public bool Failed => exception != null;
|
||||
|
||||
public Exception Exception => exception;
|
||||
|
||||
public object Result => result;
|
||||
|
||||
public static AsyncReply<T> FromResult<T>(T result) => new AsyncReply<T>(result);
|
||||
|
||||
public object Wait()
|
||||
{
|
||||
if (resultReady)
|
||||
return result;
|
||||
ManualResetEventSlim waiter;
|
||||
|
||||
mutex.WaitOne();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//int timeoutMilliseconds = 0;
|
||||
public void Timeout(int milliseconds, Action callback = null)
|
||||
{
|
||||
|
||||
//timeoutMilliseconds = milliseconds;
|
||||
|
||||
Task.Delay(milliseconds).ContinueWith(x =>
|
||||
lock (asyncLock)
|
||||
{
|
||||
if (!resultReady && exception == null)
|
||||
{
|
||||
TriggerError(new AsyncException(ErrorType.Management,
|
||||
(ushort)ExceptionCode.Timeout, "Execution timeout expired."));
|
||||
if (resultReady)
|
||||
return result;
|
||||
if (exception != null)
|
||||
throw observedException ?? exception;
|
||||
|
||||
callback?.Invoke();
|
||||
}
|
||||
});
|
||||
waiter = completionEvent ?? (completionEvent = new ManualResetEventSlim(false));
|
||||
}
|
||||
|
||||
waiter.Wait();
|
||||
return GetWaitResult();
|
||||
}
|
||||
|
||||
public object Wait(int millisecondsTimeout)
|
||||
{
|
||||
if (resultReady)
|
||||
return result;
|
||||
ManualResetEventSlim waiter;
|
||||
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Wait");
|
||||
|
||||
if (!mutex.WaitOne(millisecondsTimeout))
|
||||
{
|
||||
var e = new Exception("AsyncReply timeout");
|
||||
TriggerError(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Wait ended");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public object Result
|
||||
{
|
||||
get { return result; }
|
||||
}
|
||||
|
||||
|
||||
protected string codePath, codeMethod;
|
||||
protected int codeLine;
|
||||
|
||||
public AsyncReply Then(Action<object> callback, [CallerMemberName] string methodName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
if (codeLine == 0)
|
||||
{
|
||||
codeLine = lineNumber; codeMethod = methodName; codePath = filePath;
|
||||
}
|
||||
//lock (callbacksLock)
|
||||
//{
|
||||
lock (asyncLock)
|
||||
{
|
||||
// trace = new StackTrace();
|
||||
if (resultReady)
|
||||
return result;
|
||||
if (exception != null)
|
||||
throw observedException ?? exception;
|
||||
|
||||
waiter = completionEvent ?? (completionEvent = new ManualResetEventSlim(false));
|
||||
}
|
||||
|
||||
if (!waiter.Wait(millisecondsTimeout))
|
||||
{
|
||||
var timeoutException = new Exception("AsyncReply timeout");
|
||||
|
||||
// If completion won the timeout race, return that terminal state. Otherwise
|
||||
// retain the timeout on the reply and preserve Wait's historical exception.
|
||||
if (TrySetException(timeoutException))
|
||||
throw timeoutException;
|
||||
}
|
||||
|
||||
return GetWaitResult();
|
||||
}
|
||||
|
||||
public void Timeout(int milliseconds, Action callback = null)
|
||||
{
|
||||
_ = Task.Delay(milliseconds).ContinueWith(_ =>
|
||||
{
|
||||
var timeoutException = new AsyncException(
|
||||
ErrorType.Management,
|
||||
(ushort)ExceptionCode.Timeout,
|
||||
"Execution timeout expired.");
|
||||
|
||||
if (TrySetException(timeoutException))
|
||||
callback?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
public AsyncReply Then(
|
||||
Action<object> callback,
|
||||
[CallerMemberName] string methodName = null,
|
||||
[CallerFilePath] string filePath = null,
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
object completedResult = null;
|
||||
var invokeImmediately = false;
|
||||
|
||||
lock (asyncLock)
|
||||
{
|
||||
if (codeLine == 0)
|
||||
{
|
||||
codeLine = lineNumber;
|
||||
codeMethod = methodName;
|
||||
codePath = filePath;
|
||||
}
|
||||
|
||||
if (resultReady)
|
||||
{
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Then ready");
|
||||
|
||||
callback(result);
|
||||
return this;
|
||||
completedResult = result;
|
||||
invokeImmediately = true;
|
||||
}
|
||||
else if (exception == null)
|
||||
{
|
||||
(callbacks ?? (callbacks = new List<Action<object>>())).Add(callback);
|
||||
}
|
||||
|
||||
|
||||
//timeout = new Timer(x =>
|
||||
//{
|
||||
// // Get calling method name
|
||||
// Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
|
||||
|
||||
// var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
|
||||
// timeout.Dispose();
|
||||
|
||||
// tr = trace.ToString();
|
||||
// throw new Exception("Request timeout " + Id);
|
||||
//}, null, 15000, 0);
|
||||
|
||||
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Then pending");
|
||||
|
||||
callbacks.Add(callback);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (invokeImmediately)
|
||||
callback(completedResult);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public AsyncReply Error(Action<AsyncException> callback)
|
||||
{
|
||||
AsyncException completedException = null;
|
||||
|
||||
if (errorCallbacks == null)
|
||||
errorCallbacks = new List<Action<AsyncException>>();
|
||||
lock (asyncLock)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
completedException = exception;
|
||||
}
|
||||
else if (!resultReady)
|
||||
{
|
||||
(errorCallbacks ?? (errorCallbacks = new List<Action<AsyncException>>())).Add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
errorCallbacks.Add(callback);
|
||||
|
||||
if (exception != null)
|
||||
callback(exception);
|
||||
if (completedException != null)
|
||||
callback(completedException);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncReply Progress(Action<ProgressType, uint, uint> callback)
|
||||
{
|
||||
if (progressCallbacks == null)
|
||||
progressCallbacks = new List<Action<ProgressType, uint, uint>>();
|
||||
lock (asyncLock)
|
||||
(progressCallbacks ?? (progressCallbacks = new List<Action<ProgressType, uint, uint>>())).Add(callback);
|
||||
|
||||
progressCallbacks.Add(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncReply Warning(Action<byte, string> callback)
|
||||
{
|
||||
if (warningCallbacks == null)
|
||||
warningCallbacks = new List<Action<byte, string>>();
|
||||
lock (asyncLock)
|
||||
(warningCallbacks ?? (warningCallbacks = new List<Action<byte, string>>())).Add(callback);
|
||||
|
||||
warningCallbacks.Add(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncReply Chunk(Action<object> callback)
|
||||
{
|
||||
if (chunkCallbacks == null)
|
||||
chunkCallbacks = new List<Action<object>>();
|
||||
lock (asyncLock)
|
||||
(chunkCallbacks ?? (chunkCallbacks = new List<Action<object>>())).Add(callback);
|
||||
|
||||
chunkCallbacks.Add(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncReply Propagation(Action<object> callback)
|
||||
{
|
||||
if (propagationCallbacks == null)
|
||||
propagationCallbacks = new List<Action<object>>();
|
||||
lock (asyncLock)
|
||||
(propagationCallbacks ?? (propagationCallbacks = new List<Action<object>>())).Add(callback);
|
||||
|
||||
propagationCallbacks.Add(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Trigger(object result)
|
||||
{
|
||||
Action<object> singleCallback = null;
|
||||
Action<object>[] registeredCallbacks = null;
|
||||
var callbackCount = 0;
|
||||
ManualResetEventSlim waiter;
|
||||
|
||||
lock (asyncLock)
|
||||
{
|
||||
if (exception != null || resultReady)
|
||||
return;
|
||||
|
||||
ReadyTime = DateTime.Now;
|
||||
|
||||
//timeout?.Dispose();
|
||||
|
||||
if (exception != null)
|
||||
return;
|
||||
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Trigger");
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
this.result = result;
|
||||
|
||||
resultReady = true;
|
||||
|
||||
//if (mutex != null)
|
||||
mutex.Set();
|
||||
|
||||
foreach (var cb in callbacks)
|
||||
cb(result);
|
||||
|
||||
|
||||
//if (Debug)
|
||||
// Console.WriteLine($"AsyncReply: {Id} Trigger ended");
|
||||
// AsyncQueue deliberately resets resultReady between deliveries. Snapshot a
|
||||
// multi-callback list before unlocking, while keeping its usual one-callback
|
||||
// delivery path allocation-free.
|
||||
if (callbacks != null)
|
||||
{
|
||||
callbackCount = callbacks.Count;
|
||||
if (callbackCount == 1)
|
||||
singleCallback = callbacks[0];
|
||||
else if (callbackCount > 1)
|
||||
registeredCallbacks = callbacks.ToArray();
|
||||
}
|
||||
|
||||
waiter = completionEvent;
|
||||
}
|
||||
|
||||
return;
|
||||
waiter?.Set();
|
||||
|
||||
if (callbackCount == 1)
|
||||
singleCallback(result);
|
||||
else if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(result);
|
||||
}
|
||||
|
||||
public virtual void TriggerError(Exception exception)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
TrySetException(exception);
|
||||
}
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
if (exception is AsyncException)
|
||||
this.exception = exception as AsyncException;
|
||||
else
|
||||
this.exception = new AsyncException(exception);
|
||||
|
||||
if (errorCallbacks != null)
|
||||
{
|
||||
foreach (var cb in errorCallbacks)
|
||||
cb(this.exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no error handlers found
|
||||
throw exception;
|
||||
}
|
||||
|
||||
mutex?.Set();
|
||||
internal void TriggerErrorFromBuilder(Exception exception, bool preserveSourceForAwait)
|
||||
{
|
||||
TrySetException(exception, preserveSourceForAwait);
|
||||
}
|
||||
|
||||
internal Exception GetExceptionForAwait()
|
||||
{
|
||||
lock (asyncLock)
|
||||
return observedException ?? exception;
|
||||
}
|
||||
|
||||
public void TriggerProgress(ProgressType type, uint value, uint max)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
Action<ProgressType, uint, uint>[] registeredCallbacks;
|
||||
|
||||
if (progressCallbacks != null)
|
||||
foreach (var cb in progressCallbacks)
|
||||
cb(type, value, max);
|
||||
lock (asyncLock)
|
||||
registeredCallbacks = progressCallbacks?.ToArray();
|
||||
|
||||
return;
|
||||
if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(type, value, max);
|
||||
}
|
||||
|
||||
public void TriggerWarning(byte level, string message)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
Action<byte, string>[] registeredCallbacks;
|
||||
|
||||
if (warningCallbacks != null)
|
||||
foreach (var cb in warningCallbacks)
|
||||
cb(level, message);
|
||||
lock (asyncLock)
|
||||
registeredCallbacks = warningCallbacks?.ToArray();
|
||||
|
||||
return ;
|
||||
if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(level, message);
|
||||
}
|
||||
|
||||
|
||||
public void TriggerPropagation(object value)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
Action<object>[] registeredCallbacks;
|
||||
|
||||
if (propagationCallbacks != null)
|
||||
foreach (var cb in propagationCallbacks)
|
||||
cb(value);
|
||||
lock (asyncLock)
|
||||
registeredCallbacks = propagationCallbacks?.ToArray();
|
||||
|
||||
return;
|
||||
if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void TriggerChunk(object value)
|
||||
{
|
||||
Action<object>[] registeredCallbacks;
|
||||
|
||||
//timeout?.Dispose();
|
||||
|
||||
|
||||
if (chunkCallbacks != null)
|
||||
foreach (var cb in chunkCallbacks)
|
||||
cb(value);
|
||||
|
||||
lock (asyncLock)
|
||||
registeredCallbacks = chunkCallbacks?.ToArray();
|
||||
|
||||
if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(value);
|
||||
}
|
||||
|
||||
public AsyncAwaiter GetAwaiter()
|
||||
{
|
||||
return new AsyncAwaiter(this);
|
||||
}
|
||||
|
||||
|
||||
public AsyncAwaiter GetAwaiter() => new AsyncAwaiter(this);
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
// this.Debug = true;
|
||||
Id = MaxId++;
|
||||
Id = Interlocked.Increment(ref MaxId) - 1;
|
||||
}
|
||||
|
||||
public AsyncReply(object result)
|
||||
{
|
||||
// this.Debug = true;
|
||||
|
||||
ReadyTime = DateTime.Now;
|
||||
|
||||
resultReady = true;
|
||||
this.result = result;
|
||||
|
||||
Id = MaxId++;
|
||||
Id = Interlocked.Increment(ref MaxId) - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
public AsyncReply<T> Then(Action<T> callback)
|
||||
private object GetWaitResult()
|
||||
{
|
||||
base.Then(new Action<object>(o => callback((T)o)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Trigger(T result)
|
||||
{
|
||||
Trigger((object)result);
|
||||
}
|
||||
|
||||
public Task<bool> MoveNext(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public new Task<T> Task
|
||||
{
|
||||
get
|
||||
lock (asyncLock)
|
||||
{
|
||||
return base.Task.ContinueWith<T>((t) =>
|
||||
{
|
||||
if (exception != null)
|
||||
throw observedException ?? exception;
|
||||
|
||||
#if NETSTANDARD
|
||||
return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
|
||||
#else
|
||||
return (T)t.GetType().GetProperty("Result").GetValue(t);
|
||||
#endif
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public T Current => throw new NotImplementedException();
|
||||
|
||||
public AsyncReply(T result)
|
||||
: base(result)
|
||||
private bool TrySetException(Exception sourceException, bool preserveSourceForAwait = false)
|
||||
{
|
||||
AsyncException asyncException;
|
||||
Action<AsyncException> singleCallback = null;
|
||||
Action<AsyncException>[] registeredCallbacks = null;
|
||||
var callbackCount = 0;
|
||||
ManualResetEventSlim waiter;
|
||||
|
||||
lock (asyncLock)
|
||||
{
|
||||
if (resultReady || exception != null)
|
||||
return false;
|
||||
|
||||
asyncException = sourceException as AsyncException ?? new AsyncException(sourceException);
|
||||
observedException = preserveSourceForAwait ? sourceException : asyncException;
|
||||
exception = asyncException;
|
||||
|
||||
if (errorCallbacks != null)
|
||||
{
|
||||
callbackCount = errorCallbacks.Count;
|
||||
if (callbackCount == 1)
|
||||
singleCallback = errorCallbacks[0];
|
||||
else if (callbackCount > 1)
|
||||
registeredCallbacks = errorCallbacks.ToArray();
|
||||
}
|
||||
|
||||
waiter = completionEvent;
|
||||
}
|
||||
|
||||
waiter?.Set();
|
||||
|
||||
if (callbackCount == 1)
|
||||
singleCallback(asyncException);
|
||||
else if (registeredCallbacks != null)
|
||||
foreach (var callback in registeredCallbacks)
|
||||
callback(asyncException);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
public class AsyncReplyBuilder
|
||||
{
|
||||
AsyncReply reply;
|
||||
int hasSuspended;
|
||||
|
||||
AsyncReplyBuilder(AsyncReply reply)
|
||||
{
|
||||
@@ -33,7 +35,9 @@ public class AsyncReplyBuilder
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
reply.TriggerError(exception);
|
||||
reply.TriggerErrorFromBuilder(
|
||||
exception,
|
||||
preserveSourceForAwait: Volatile.Read(ref hasSuspended) == 0);
|
||||
}
|
||||
|
||||
public void SetResult()
|
||||
@@ -46,6 +50,7 @@ public class AsyncReplyBuilder
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
Volatile.Write(ref hasSuspended, 1);
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
@@ -54,6 +59,7 @@ public class AsyncReplyBuilder
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
Volatile.Write(ref hasSuspended, 1);
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
public class AsyncReplyBuilder<T>
|
||||
{
|
||||
AsyncReply<T> reply;
|
||||
int hasSuspended;
|
||||
|
||||
AsyncReplyBuilder(AsyncReply<T> reply)
|
||||
{
|
||||
@@ -33,7 +35,9 @@ public class AsyncReplyBuilder<T>
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
reply.TriggerError(exception);
|
||||
reply.TriggerErrorFromBuilder(
|
||||
exception,
|
||||
preserveSourceForAwait: Volatile.Read(ref hasSuspended) == 0);
|
||||
}
|
||||
|
||||
public void SetResult(T result)
|
||||
@@ -46,6 +50,7 @@ public class AsyncReplyBuilder<T>
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
Volatile.Write(ref hasSuspended, 1);
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
@@ -54,6 +59,7 @@ public class AsyncReplyBuilder<T>
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
Volatile.Write(ref hasSuspended, 1);
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
@@ -22,31 +22,22 @@ SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Esiur.Resource;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Esiur.Core;
|
||||
|
||||
[AsyncMethodBuilder(typeof(AsyncReplyBuilder<>))]
|
||||
public class AsyncReply<T> : AsyncReply
|
||||
{
|
||||
|
||||
public AsyncReply<T> Then(Action<T> callback, [CallerMemberName] string methodName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
|
||||
public AsyncReply<T> Then(
|
||||
Action<T> callback,
|
||||
[CallerMemberName] string methodName = null,
|
||||
[CallerFilePath] string filePath = null,
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
if (base.codeLine == 0)
|
||||
{
|
||||
base.codeLine = lineNumber; base.codeMethod = methodName; base.codePath = filePath;
|
||||
}
|
||||
|
||||
base.Then((x) => callback((T)x));
|
||||
base.Then(value => callback((T)value), methodName, filePath, lineNumber);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -56,329 +47,25 @@ public class AsyncReply<T> : AsyncReply
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public AsyncReply<T> Chunk(Action<T> callback)
|
||||
{
|
||||
chunkCallbacks.Add((x) => callback((T)x));
|
||||
base.Chunk(value => callback((T)value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncReply(T result)
|
||||
: base(result)
|
||||
: base(result)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public AsyncReply()
|
||||
: base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public new AsyncAwaiter<T> GetAwaiter()
|
||||
{
|
||||
return new AsyncAwaiter<T>(this);
|
||||
}
|
||||
|
||||
public new T Wait()
|
||||
{
|
||||
return (T)base.Wait();
|
||||
}
|
||||
|
||||
public new T Wait(int millisecondsTimeout)
|
||||
{
|
||||
return (T)base.Wait(millisecondsTimeout);
|
||||
}
|
||||
|
||||
/*
|
||||
protected new List<Action> callbacks = new List<Action>();
|
||||
protected new object result;
|
||||
|
||||
protected new List<Action<AsyncException>> errorCallbacks = new List<Action<AsyncException>>();
|
||||
|
||||
protected new List<Action<ProgressType, int, int>> progressCallbacks = new List<Action<ProgressType, int, int>>();
|
||||
|
||||
protected new List<Action> chunkCallbacks = new List<Action>();
|
||||
|
||||
//List<AsyncAwaiter> awaiters = new List<AsyncAwaiter>();
|
||||
|
||||
object asyncLock = new object();
|
||||
|
||||
//public Timer timeout;// = new Timer()
|
||||
|
||||
AsyncException exception;
|
||||
// StackTrace trace;
|
||||
AutoResetEvent mutex = new AutoResetEvent(false);
|
||||
|
||||
public static int MaxId;
|
||||
|
||||
public int Id;
|
||||
|
||||
public bool Ready
|
||||
{
|
||||
get { return resultReady; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public T Wait()
|
||||
{
|
||||
|
||||
if (resultReady)
|
||||
return result;
|
||||
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Wait");
|
||||
|
||||
//mutex = new AutoResetEvent(false);
|
||||
mutex.WaitOne();
|
||||
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Wait ended");
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public object Result
|
||||
{
|
||||
get { return result; }
|
||||
}
|
||||
|
||||
|
||||
public IAsyncReply<T> Then(Action<T> callback)
|
||||
{
|
||||
//lock (callbacksLock)
|
||||
//{
|
||||
lock (asyncLock)
|
||||
{
|
||||
// trace = new StackTrace();
|
||||
|
||||
if (resultReady)
|
||||
{
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Then ready");
|
||||
|
||||
callback(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
//timeout = new Timer(x =>
|
||||
//{
|
||||
// // Get calling method name
|
||||
// Console.WriteLine(trace.GetFrame(1).GetMethod().Name);
|
||||
|
||||
// var tr = String.Join("\r\n", trace.GetFrames().Select(f => f.GetMethod().Name));
|
||||
// timeout.Dispose();
|
||||
|
||||
// tr = trace.ToString();
|
||||
// throw new Exception("Request timeout " + Id);
|
||||
//}, null, 15000, 0);
|
||||
|
||||
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Then pending");
|
||||
|
||||
|
||||
|
||||
callbacks.Add(callback);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IAsyncReply<T> Error(Action<AsyncException> callback)
|
||||
{
|
||||
// lock (callbacksLock)
|
||||
// {
|
||||
errorCallbacks.Add(callback);
|
||||
|
||||
if (exception != null)
|
||||
callback(exception);
|
||||
|
||||
return this;
|
||||
//}
|
||||
}
|
||||
|
||||
public IAsyncReply<T> Progress(Action<ProgressType, int, int> callback)
|
||||
{
|
||||
//lock (callbacksLock)
|
||||
//{
|
||||
progressCallbacks.Add(callback);
|
||||
return this;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public IAsyncReply<T> Chunk(Action<T> callback)
|
||||
{
|
||||
// lock (callbacksLock)
|
||||
// {
|
||||
chunkCallbacks.Add(callback);
|
||||
return this;
|
||||
// }
|
||||
}
|
||||
|
||||
public void Trigger(object result)
|
||||
{
|
||||
lock (asyncLock)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Trigger");
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
this.result = (T)result;
|
||||
|
||||
resultReady = true;
|
||||
|
||||
//if (mutex != null)
|
||||
mutex.Set();
|
||||
|
||||
foreach (var cb in callbacks)
|
||||
cb((T)result);
|
||||
|
||||
|
||||
if (Debug)
|
||||
Console.WriteLine($"AsyncReply: {Id} Trigger ended");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void TriggerError(Exception exception)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
if (exception is AsyncException)
|
||||
this.exception = exception as AsyncException;
|
||||
else
|
||||
this.exception = new AsyncException(ErrorType.Management, 0, exception.Message);
|
||||
|
||||
|
||||
// lock (callbacksLock)
|
||||
// {
|
||||
foreach (var cb in errorCallbacks)
|
||||
cb(this.exception);
|
||||
// }
|
||||
|
||||
mutex?.Set();
|
||||
|
||||
}
|
||||
|
||||
public void TriggerProgress(ProgressType type, int value, int max)
|
||||
{
|
||||
//timeout?.Dispose();
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
//lock (callbacksLock)
|
||||
//{
|
||||
foreach (var cb in progressCallbacks)
|
||||
cb(type, value, max);
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public void TriggerChunk(object value)
|
||||
{
|
||||
|
||||
//timeout?.Dispose();
|
||||
|
||||
if (resultReady)
|
||||
return;
|
||||
|
||||
//lock (callbacksLock)
|
||||
//{
|
||||
foreach (var cb in chunkCallbacks)
|
||||
cb((T)value);
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
public AsyncAwaiter<T> GetAwaiter()
|
||||
{
|
||||
return new AsyncAwaiter<T>(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
// this.Debug = true;
|
||||
Id = MaxId++;
|
||||
}
|
||||
|
||||
public AsyncReply(T result)
|
||||
{
|
||||
// this.Debug = true;
|
||||
resultReady = true;
|
||||
this.result = result;
|
||||
|
||||
Id = MaxId++;
|
||||
}
|
||||
|
||||
/*
|
||||
public AsyncReply<T> Then(Action<T> callback)
|
||||
{
|
||||
base.Then(new Action<object>(o => callback((T)o)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Trigger(T result)
|
||||
{
|
||||
Trigger((object)result);
|
||||
}
|
||||
|
||||
public Task<bool> MoveNext(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public AsyncReply()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public new Task<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Task.ContinueWith<T>((t) =>
|
||||
{
|
||||
|
||||
#if NETSTANDARD
|
||||
return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
|
||||
#else
|
||||
return (T)t.GetType().GetProperty("Result").GetValue(t);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public T Current => throw new NotImplementedException();
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
public new AsyncAwaiter<T> GetAwaiter() => new AsyncAwaiter<T>(this);
|
||||
|
||||
public new T Wait() => (T)base.Wait();
|
||||
|
||||
public new T Wait(int millisecondsTimeout) => (T)base.Wait(millisecondsTimeout);
|
||||
}
|
||||
|
||||
@@ -204,9 +204,7 @@ public class AsyncStreamReply : AsyncReply
|
||||
}
|
||||
|
||||
OnStreamError(exception);
|
||||
|
||||
if (errorCallbacks != null)
|
||||
base.TriggerError(exception);
|
||||
base.TriggerError(exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user