Wrapping ManualResetEvent as awaitable task - c#

I'd like to await on a manual reset event with time-out and observing cancellation. I've come up with something like below. The manual reset event object is provided by an API beyond my control. Is there a way to make this happen without taking on and blocking a thread from ThreadPool?
static Task<bool> TaskFromWaitHandle(WaitHandle mre, int timeout, CancellationToken ct)
{
return Task.Run(() =>
{
bool s = WaitHandle.WaitAny(new WaitHandle[] { mre, ct.WaitHandle }, timeout) == 0;
ct.ThrowIfCancellationRequested();
return s;
}, ct);
}
// ...
if (await TaskFromWaitHandle(manualResetEvent, 1000, cts.Token))
{
// true if event was set
}
else
{
// false if timed out, exception if cancelled
}
[EDITED] Apparently, it makes sense to use RegisterWaitForSingleObject. I'll give it a try.

RegisterWaitForSingleObject will combine waits onto dedicated waiter threads, each of which can wait on multiple handles (specifically, 63 of them, which is MAXIMUM_WAIT_OBJECTS minus one for a "control" handle).
So you should be able to use something like this (warning: untested):
public static class WaitHandleExtensions
{
public static Task AsTask(this WaitHandle handle)
{
return AsTask(handle, Timeout.InfiniteTimeSpan);
}
public static Task AsTask(this WaitHandle handle, TimeSpan timeout)
{
var tcs = new TaskCompletionSource<object>();
var registration = ThreadPool.RegisterWaitForSingleObject(handle, (state, timedOut) =>
{
var localTcs = (TaskCompletionSource<object>)state;
if (timedOut)
localTcs.TrySetCanceled();
else
localTcs.TrySetResult(null);
}, tcs, timeout, executeOnlyOnce: true);
tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state).Unregister(null), registration, TaskScheduler.Default);
return tcs.Task;
}
}

You also can use SemaphoreSlim.WaitAsync() which is similar to ManualResetEvent

Stephen's Cleary solution looks perfect. Microsoft provides the similar one.
As I haven't seen an example with cancellation logic.
Here it is:
public static class WaitHandleExtensions
{
public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken, int timeoutMilliseconds = Timeout.Infinite)
{
if (waitHandle == null)
throw new ArgumentNullException(nameof(waitHandle));
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
CancellationTokenRegistration ctr = cancellationToken.Register(() => tcs.TrySetCanceled());
TimeSpan timeout = timeoutMilliseconds > Timeout.Infinite ? TimeSpan.FromMilliseconds(timeoutMilliseconds) : Timeout.InfiniteTimeSpan;
RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
(_, timedOut) =>
{
if (timedOut)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(true);
}
},
null, timeout, true);
Task<bool> task = tcs.Task;
_ = task.ContinueWith(_ =>
{
rwh.Unregister(null);
return ctr.Unregister();
}, CancellationToken.None);
return task;
}
}

You can give this one a shot, https://www.badflyer.com/asyncmanualresetevent , tried to build upon the example on https://blogs.msdn.com/b/pfxteam/archive/2012/02/11/10266920.aspx to support timeouts and cancellation.
using System;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// An async manual reset event.
/// </summary>
public sealed class ManualResetEventAsync
{
// Inspiration from https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-1-asyncmanualresetevent/
// and the .net implementation of SemaphoreSlim
/// <summary>
/// The timeout in milliseconds to wait indefinitly.
/// </summary>
private const int WaitIndefinitly = -1;
/// <summary>
/// True to run synchronous continuations on the thread which invoked Set. False to run them in the threadpool.
/// </summary>
private readonly bool runSynchronousContinuationsOnSetThread = true;
/// <summary>
/// The current task completion source.
/// </summary>
private volatile TaskCompletionSource<bool> completionSource = new TaskCompletionSource<bool>();
/// <summary>
/// Initializes a new instance of the <see cref="ManualResetEventAsync"/> class.
/// </summary>
/// <param name="isSet">True to set the task completion source on creation.</param>
public ManualResetEventAsync(bool isSet)
: this(isSet: isSet, runSynchronousContinuationsOnSetThread: true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ManualResetEventAsync"/> class.
/// </summary>
/// <param name="isSet">True to set the task completion source on creation.</param>
/// <param name="runSynchronousContinuationsOnSetThread">If you have synchronous continuations, they will run on the thread which invokes Set, unless you set this to false.</param>
public ManualResetEventAsync(bool isSet, bool runSynchronousContinuationsOnSetThread)
{
this.runSynchronousContinuationsOnSetThread = runSynchronousContinuationsOnSetThread;
if (isSet)
{
this.completionSource.TrySetResult(true);
}
}
/// <summary>
/// Wait for the manual reset event.
/// </summary>
/// <returns>A task which completes when the event is set.</returns>
public Task WaitAsync()
{
return this.AwaitCompletion(ManualResetEventAsync.WaitIndefinitly, default(CancellationToken));
}
/// <summary>
/// Wait for the manual reset event.
/// </summary>
/// <param name="token">A cancellation token.</param>
/// <returns>A task which waits for the manual reset event.</returns>
public Task WaitAsync(CancellationToken token)
{
return this.AwaitCompletion(ManualResetEventAsync.WaitIndefinitly, token);
}
/// <summary>
/// Wait for the manual reset event.
/// </summary>
/// <param name="timeout">A timeout.</param>
/// <param name="token">A cancellation token.</param>
/// <returns>A task which waits for the manual reset event. Returns true if the timeout has not expired. Returns false if the timeout expired.</returns>
public Task<bool> WaitAsync(TimeSpan timeout, CancellationToken token)
{
return this.AwaitCompletion((int)timeout.TotalMilliseconds, token);
}
/// <summary>
/// Wait for the manual reset event.
/// </summary>
/// <param name="timeout">A timeout.</param>
/// <returns>A task which waits for the manual reset event. Returns true if the timeout has not expired. Returns false if the timeout expired.</returns>
public Task<bool> WaitAsync(TimeSpan timeout)
{
return this.AwaitCompletion((int)timeout.TotalMilliseconds, default(CancellationToken));
}
/// <summary>
/// Set the completion source.
/// </summary>
public void Set()
{
if (this.runSynchronousContinuationsOnSetThread)
{
this.completionSource.TrySetResult(true);
}
else
{
// Run synchronous completions in the thread pool.
Task.Run(() => this.completionSource.TrySetResult(true));
}
}
/// <summary>
/// Reset the manual reset event.
/// </summary>
public void Reset()
{
// Grab a reference to the current completion source.
var currentCompletionSource = this.completionSource;
// Check if there is nothing to be done, return.
if (!currentCompletionSource.Task.IsCompleted)
{
return;
}
// Otherwise, try to replace it with a new completion source (if it is the same as the reference we took before).
Interlocked.CompareExchange(ref this.completionSource, new TaskCompletionSource<bool>(), currentCompletionSource);
}
/// <summary>
/// Await completion based on a timeout and a cancellation token.
/// </summary>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <param name="token">The cancellation token.</param>
/// <returns>A task (true if wait succeeded). (False on timeout).</returns>
private async Task<bool> AwaitCompletion(int timeoutMS, CancellationToken token)
{
// Validate arguments.
if (timeoutMS < -1 || timeoutMS > int.MaxValue)
{
throw new ArgumentException("The timeout must be either -1ms (indefinitely) or a positive ms value <= int.MaxValue");
}
CancellationTokenSource timeoutToken = null;
// If the token cannot be cancelled, then we dont need to create any sort of linked token source.
if (false == token.CanBeCanceled)
{
// If the wait is indefinite, then we don't need to create a second task at all to wait on, just wait for set.
if (timeoutMS == -1)
{
return await this.completionSource.Task;
}
timeoutToken = new CancellationTokenSource();
}
else
{
// A token source which will get canceled either when we cancel it, or when the linked token source is canceled.
timeoutToken = CancellationTokenSource.CreateLinkedTokenSource(token);
}
using (timeoutToken)
{
// Create a task to account for our timeout. The continuation just eats the task cancelled exception, but makes sure to observe it.
Task delayTask = Task.Delay(timeoutMS, timeoutToken.Token).ContinueWith((result) => { var e = result.Exception; }, TaskContinuationOptions.ExecuteSynchronously);
var resultingTask = await Task.WhenAny(this.completionSource.Task, delayTask).ConfigureAwait(false);
// The actual task finished, not the timeout, so we can cancel our cancellation token and return true.
if (resultingTask != delayTask)
{
// Cancel the timeout token to cancel the delay if it is still going.
timeoutToken.Cancel();
return true;
}
// Otherwise, the delay task finished. So throw if it finished because it was canceled.
token.ThrowIfCancellationRequested();
return false;
}
}
}

Alternative solution: wait for the handles of the task and the manual reset event
I was having memory leaks when using Task.WaitAny() with a Task (returned by SqlConnection.OpenAsync()') and a Manual Reset Event received as parameter and wrapped in a Task with AsTask(). These object were not being disposed: TaskCompletionSource<Object>, Task<Object>, StandardTaskContinuation, RegisteredWaitHandle, RegisteredWaithandleSafe, ContinuationResultTaskFromresultTask<Object,bool>, _ThreadPoolWaitOrTimerCallback).
This is real production code, used in a Windows service, of a function that tries to open a connection to a db in a loop until the connection is opened, or the operation fails, or the ManualResetEvent _finishRequest, received as parameter in the function containing this code, is signaled by code in any other thread.
To avoid the leak, I decided to do it the other way round: wait for the handles of the _finishRequest and the Task returned by OpenAsync():
Task asyncOpening = sqlConnection.OpenAsync();
// Wait for the async open to finish, or until _finishRequest is signaled
var waitHandles = new WaitHandle[]
{
// index 0 in array: extract the AsyncWaitHandle from the Task
((IAsyncResult)asyncOpening).AsyncWaitHandle,
// index 1:
_finishRequest
};
// Check if finish was requested (index of signaled handle in the array = 1)
int whichFinished = WaitHandle.WaitAny(waitHandles);
finishRequested = whichFinished == 1;
// If so, break the loop to exit immediately
if (finishRequested)
break;
// If not, check if OpenAsync finished with error (it's a Task)
if (asyncOpening.IsFaulted)
{
// Extract the exception from the task, and throw it
// NOTE: adapt it to your case. In mine, I'm interested in the inner exception,
// but you can check the exception itself, for example to see if it was a timeout,
// if you specified it in the call to the async function that returns the Task
var ex = asyncOpening?.Exception?.InnerExceptions?[0];
if (ex != null) throw ex;
}
else
{
Log.Verbose("Connection to database {Database} on server {Server}", database, server);
break;
}
If you also need the timeout, you can include it in the call to OpenAsync, or you asyn function, and then check if the result of the async operation was cancelled because of the timeout: check the status of the Task when finished, as you can see in the NOTE in the code comment.

Related

TaskCompletionSource with Token

I have created scan method using Scandit framework. Since I am using Xamarin.Forms there is no option to await the scan. Scandit prepared the callback as result of scanning. I would like to wrap that into one method which could be used with awaiter.
There is what I have done:
public async Task<Operation<string>> ScanAsync(CancellationToken cancellationToken = default(CancellationToken))
{
try
{
_codeScannedTcs = new TaskCompletionSource<Operation<string>>();
ScanditService.BarcodePicker.DidScan += OnScanFinished;
await ScanditService.BarcodePicker.StartScanningAsync();
var result = await Task.WhenAny(_codeScannedTcs.Task);
return result.Result;
}
catch (TaskCanceledException)
{
return Operation<string>.FailedOperation("A task was canceled");
}
catch (Exception ex)
{
return Operation<string>.FailedOperation(ex.Message);
}
finally
{
_codeScannedTcs = null;
ScanditService.BarcodePicker.DidScan -= OnScanFinished;
}
}
How in that situation I can use the cancellation token to break the scanning in any time?
I've used this code in the past:
/// <summary>
/// Allows the code waiting for an async operation to stop waiting when the cancellation token is set.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="task"></param>
/// <param name="cancellationToken"></param>
/// <see cref="https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/"/>
/// <returns></returns>
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(x => ((TaskCompletionSource<bool>)x).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
}
return await task;
}
It is either verbatim copy pasted from a microsoft blog post or derivative of it

Async task within MvvmCross Command not returning

I am building a small project using MvvMCross within a Xamarin PCL project and having issue with an async Task that I am calling within a command that is bound to a button.
I have a fake web service where-in I simply call Task.Delay(3000). When the process gets to this point it simply sits and does nothing.
I originally had the command call using the .wait() call but read somewhere that this was a blocking call and cant be miced with "async / wait"
Could someone help and possible give me a hint as to where I am going wrong on the command binding please ?
https://bitbucket.org/johncogan/exevaxamarinapp is the public git repo, the specific command is
public ICommand SaveProfile
within the ProfileViewModel.cs file.
The specific code is:
public ICommand SaveProfile
{
get
{
return new MvxCommand(() =>
{
if (_profile.IsValidData())
{
// Wait for task to compelte, do UI updates here
// TODO Throbber / Spinner
EnumWebServiceResult taskResult;
Mvx.Resolve<IProfileWebService>().SendProfileToServer(_profile).Wait();
if(_profileWebService.getLastResponseResult() == true){
taskResult = EnumWebServiceResult.SUCCESS;
}else{
taskResult = EnumWebServiceResult.FAILED_UNKNOWN;
}
//_profileWebService.SendProfileToServer(_profile).Wait();
// Close(this);
}
});
}
}
The web service class () is:
using System;
using System.Threading.Tasks;
using ExevaXamarinApp.Models;
namespace ExevaXamarinApp.Services
{
public class FakeProfileWebService : IProfileWebService
{
public int _delayPeriod { get; private set; }
public bool? lastResult;
/// <summary>
/// Initializes a new instance of the <see cref="T:ExevaXamarinApp.Enumerations.FakeProfileWebService"/> class.
/// </summary>
/// 3 second delay to simulate a remote request
public FakeProfileWebService()
{
_delayPeriod = 3000;
lastResult = null;
}
private Task Sleep()
{
return Task.Delay(3000);
}
public bool? getLastResponseResult(){
return lastResult;
}
/// <summary>
/// Sends the profile to server asynchronously
/// </summary>
/// <returns>EnumWebServiceResultFlag value</returns>
/// <param name="profileObject">Profile model object</param>
public async Task SendProfileToServer(Profile profileObject)
{
// Validate arguments before attempting to use web serivce
if (profileObject.IsValidData())
{
// TODO: Return ENUM FLAG that represents the state of the result
await Sleep();
lastResult = true;
}else{
lastResult = false;
}
}
}
}
Please try this:
public ICommand SaveProfile
{
get
{
return new MvxCommand(async () => // async added
{
if (_profile.IsValidData())
{
// Wait for task to compelte, do UI updates here
// TODO Throbber / Spinner
EnumWebServiceResult taskResult;
await Mvx.Resolve<IProfileWebService>().SendProfileToServer(_profile).ConfigureAwait(false); // await, confi.. added
if(_profileWebService.getLastResponseResult() == true){
taskResult = EnumWebServiceResult.SUCCESS;
}else{
taskResult = EnumWebServiceResult.FAILED_UNKNOWN;
}
//_profileWebService.SendProfileToServer(_profile).Wait();
// Close(this);
}
});
}
}
private async Task Sleep() // async added
{
return await Task.Delay(3000).ConfigureAwait(false); // await, confi... added
}
public async Task SendProfileToServer(Profile profileObject)
{
// Validate arguments before attempting to use web serivce
if (profileObject.IsValidData())
{
// TODO: Return ENUM FLAG that represents the state of the result
await Sleep().ConfigureAwait(false); // await, confi... added
lastResult = true;
}else{
lastResult = false;
}
}
The problem is, that the context from the UI and the async cause a deadlock.

C# Wait until condition is true

I am trying to write a code that executes when a condition is met. Currently, I am using while...loop, which I know is not very efficient. I am also looking at AutoResetEvent() but i don't know how to implement it such that it keeps checking until the condition is true.
The code also happens to live inside an async method, so may be some kind of await could work?
private async void btnOk_Click(object sender, EventArgs e)
{
// Do some work
Task<string> task = Task.Run(() => GreatBigMethod());
string GreatBigMethod = await task;
// Wait until condition is false
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
}
// Do work
Console.WriteLine("YAY");
}
private bool isExcelInteractive()
{
try
{
Globals.ThisWorkbook.Application.Interactive = Globals.ThisWorkbook.Application.Interactive;
return true; // Excel is free
}
catch
{
return false; // Excel will throw an exception, meaning its busy
}
}
I need to find a way to keep checking isExcelInteractive() without CPU stuck in a loop.
Note: There is no event handler in Excel that would be raised when it is not in edit mode.
At least you can change your loop from a busy-wait to a slow poll. For example:
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
await Task.Delay(25);
}
Ended up writing this today and seems to be ok. Your usage could be:
await TaskEx.WaitUntil(isExcelInteractive);
code (including the inverse operation)
public static class TaskEx
{
/// <summary>
/// Blocks while condition is true or timeout occurs.
/// </summary>
/// <param name="condition">The condition that will perpetuate the block.</param>
/// <param name="frequency">The frequency at which the condition will be check, in milliseconds.</param>
/// <param name="timeout">Timeout in milliseconds.</param>
/// <exception cref="TimeoutException"></exception>
/// <returns></returns>
public static async Task WaitWhile(Func<bool> condition, int frequency = 25, int timeout = -1)
{
var waitTask = Task.Run(async () =>
{
while (condition()) await Task.Delay(frequency);
});
if(waitTask != await Task.WhenAny(waitTask, Task.Delay(timeout)))
throw new TimeoutException();
}
/// <summary>
/// Blocks until condition is true or timeout occurs.
/// </summary>
/// <param name="condition">The break condition.</param>
/// <param name="frequency">The frequency at which the condition will be checked.</param>
/// <param name="timeout">The timeout in milliseconds.</param>
/// <returns></returns>
public static async Task WaitUntil(Func<bool> condition, int frequency = 25, int timeout = -1)
{
var waitTask = Task.Run(async () =>
{
while (!condition()) await Task.Delay(frequency);
});
if (waitTask != await Task.WhenAny(waitTask,
Task.Delay(timeout)))
throw new TimeoutException();
}
}
Example usage:
https://dotnetfiddle.net/Vy8GbV
You can use thread waiting handler
private readonly System.Threading.EventWaitHandle waitHandle = new System.Threading.AutoResetEvent(false);
private void btnOk_Click(object sender, EventArgs e)
{
// Do some work
Task<string> task = Task.Run(() => GreatBigMethod());
string GreatBigMethod = await task;
// Wait until condition is false
waitHandle.WaitOne();
Console.WriteLine("Excel is busy");
waitHandle.Reset();
// Do work
Console.WriteLine("YAY");
}
then some other job need to set your handler
void isExcelInteractive()
{
/// Do your check
waitHandle.Set()
}
Update:
If you want use this solution, you have to call isExcelInteractive() continuously with specific interval:
var actions = new []{isExcelInteractive, () => Thread.Sleep(25)};
foreach (var action in actions)
{
action();
}
This implementation is totally based on Sinaesthetic's, but adding CancellationToken and keeping the same execution thread and context; that is, delegating the use of Task.Run() up to the caller depending on whether condition needs to be evaluated in the same thread or not.
Also, notice that, if you don't really need to throw a TimeoutException and breaking the loop is enough, you might want to make use of cts.CancelAfter() or new CancellationTokenSource(millisecondsDelay) instead of using timeoutTask with Task.Delay plus Task.WhenAny.
public static class AsyncUtils
{
/// <summary>
/// Blocks while condition is true or task is canceled.
/// </summary>
/// <param name="ct">
/// Cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitWhileAsync(CancellationToken ct, Func<bool> condition, int pollDelay = 25)
{
try
{
while (condition())
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
}
catch (TaskCanceledException)
{
// ignore: Task.Delay throws this exception when ct.IsCancellationRequested = true
// In this case, we only want to stop polling and finish this async Task.
}
}
/// <summary>
/// Blocks until condition is true or task is canceled.
/// </summary>
/// <param name="ct">
/// Cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitUntilAsync(CancellationToken ct, Func<bool> condition, int pollDelay = 25)
{
try
{
while (!condition())
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
}
catch (TaskCanceledException)
{
// ignore: Task.Delay throws this exception when ct.IsCancellationRequested = true
// In this case, we only want to stop polling and finish this async Task.
}
}
/// <summary>
/// Blocks while condition is true or timeout occurs.
/// </summary>
/// <param name="ct">
/// The cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <param name="timeout">
/// Timeout in milliseconds.
/// </param>
/// <exception cref="TimeoutException">
/// Thrown after timeout milliseconds
/// </exception>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitWhileAsync(CancellationToken ct, Func<bool> condition, int pollDelay, int timeout)
{
if (ct.IsCancellationRequested)
{
return;
}
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
Task waitTask = WaitWhileAsync(cts.Token, condition, pollDelay);
Task timeoutTask = Task.Delay(timeout, cts.Token);
Task finishedTask = await Task.WhenAny(waitTask, timeoutTask).ConfigureAwait(true);
if (!ct.IsCancellationRequested)
{
cts.Cancel(); // Cancel unfinished task
await finishedTask.ConfigureAwait(true); // Propagate exceptions
if (finishedTask == timeoutTask)
{
throw new TimeoutException();
}
}
}
}
/// <summary>
/// Blocks until condition is true or timeout occurs.
/// </summary>
/// <param name="ct">
/// Cancellation token
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <param name="timeout">
/// Timeout in milliseconds.
/// </param>
/// <exception cref="TimeoutException">
/// Thrown after timeout milliseconds
/// </exception>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitUntilAsync(CancellationToken ct, Func<bool> condition, int pollDelay, int timeout)
{
if (ct.IsCancellationRequested)
{
return;
}
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
Task waitTask = WaitUntilAsync(cts.Token, condition, pollDelay);
Task timeoutTask = Task.Delay(timeout, cts.Token);
Task finishedTask = await Task.WhenAny(waitTask, timeoutTask).ConfigureAwait(true);
if (!ct.IsCancellationRequested)
{
cts.Cancel(); // Cancel unfinished task
await finishedTask.ConfigureAwait(true); // Propagate exceptions
if (finishedTask == timeoutTask)
{
throw new TimeoutException();
}
}
}
}
}
Try this
async void Function()
{
while (condition)
{
await Task.Delay(1);
}
}
This will make the program wait until the condition is not true.
You can just invert it by adding a "!" infront of the condition so that it will wait until the condition is true.
you can use SpinUntil which is buildin in the .net-framework. Please note: This method causes high cpu-workload.
After digging a lot of stuff, finally, I came up with a good solution that doesn't hang the CI :) Suit it to your needs!
public static Task WaitUntil<T>(T elem, Func<T, bool> predicate, int seconds = 10)
{
var tcs = new TaskCompletionSource<int>();
using(var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)))
{
cancellationTokenSource.Token.Register(() =>
{
tcs.SetException(
new TimeoutException($"Waiting predicate {predicate} for {elem.GetType()} timed out!"));
tcs.TrySetCanceled();
});
while(!cancellationTokenSource.IsCancellationRequested)
{
try
{
if (!predicate(elem))
{
continue;
}
}
catch(Exception e)
{
tcs.TrySetException(e);
}
tcs.SetResult(0);
break;
}
return tcs.Task;
}
}
You can use an async result and a delegate for this. If you read up on the documentation it should make it pretty clear what to do. I can write up some sample code if you like and attach it to this answer.
Action isExcelInteractive = IsExcelInteractive;
private async void btnOk_Click(object sender, EventArgs e)
{
IAsyncResult result = isExcelInteractive.BeginInvoke(ItIsDone, null);
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("YAY");
}
static void IsExcelInteractive(){
while (something_is_false) // do your check here
{
if(something_is_true)
return true;
}
Thread.Sleep(1);
}
void ItIsDone(IAsyncResult result)
{
this.isExcelInteractive.EndInvoke(result);
}
Apologies if this code isn't 100% complete, I don't have Visual Studio on this computer, but hopefully it gets you where you need to get to.

Simulate a delay in execution in Unit Test using Moq

I'm trying to test the following:
protected IHealthStatus VerifyMessage(ISubscriber destination)
{
var status = new HeartBeatStatus();
var task = new Task<CheckResult>(() =>
{
Console.WriteLine("VerifyMessage(Start): {0} - {1}", DateTime.Now, WarningTimeout);
Thread.Sleep(WarningTimeout - 500);
Console.WriteLine("VerifyMessage(Success): {0}", DateTime.Now);
if (CheckMessages(destination))
{
return CheckResult.Success;
}
Console.WriteLine("VerifyMessage(Pre-Warning): {0} - {1}", DateTime.Now, ErrorTimeout);
Thread.Sleep(ErrorTimeout - 500);
Console.WriteLine("VerifyMessage(Warning): {0}", DateTime.Now);
if (CheckMessages(destination))
{
return CheckResult.Warning;
}
return CheckResult.Error;
});
task.Start();
task.Wait();
status.Status = task.Result;
return status;
}
with the following unit test:
public void HeartBeat_Should_ReturnWarning_When_MockReturnsWarning()
{
// Arrange
var heartbeat = new SocketToSocketHeartbeat(_sourceSubscriber.Object, _destinationSubscriber.Object);
heartbeat.SetTaskConfiguration(this.ConfigurationHB1ToHB2_ValidConfiguration());
// Simulate the message being delayed to destination subscriber.
_destinationSubscriber.Setup(foo => foo.ReceivedMessages).Returns(DelayDelivery(3000, Message_HB1ToHB2()));
// Act
var healthStatus = heartbeat.Execute();
// Assert
Assert.AreEqual(CheckResult.Warning, healthStatus.Status);
}
Message_HB1ToHB2() just returns a string of characters and the "Delay Delivery" method is
private List<NcsMessage> DelayDelivery(int delay, string message)
{
var sent = DateTime.Now;
var msg = new NcsMessage()
{
SourceSubscriber = "HB1",
DestinationSubscriber = "HB2",
SentOrReceived = sent,
Message = message
};
var messages = new List<NcsMessage>();
messages.Add(msg);
Console.WriteLine("DelayDelivery: {0}", DateTime.Now);
Thread.Sleep(delay);
Console.WriteLine("DelayDelivery: {0}", DateTime.Now);
return messages;
}
I'm using Moq as the mocking framework and MSTest as the testing framework. Whenever I run the unit test, I get the following output:
DelayDelivery: 04/04/2013 15:50:33
DelayDelivery: 04/04/2013 15:50:36
VerifyMessage(Start): 04/04/2013 15:50:36 - 3000
VerifyMessage(Success): 04/04/2013 15:50:38
Beyond the obvious "code smell" using the Thread.Sleep in the methods above, the result of the unit test is not what I'm trying to accomplish.
Can anyone suggest a better/accurate way to use the Moq framework to simulate a delay in "delivery" of the message. I've left out some of the "glue" code and only included the relevant parts. Let me know if something I've left out that prevents you from being able to understand the question.
If you want a Moq mock to just sit and do nothing for a while you can use a callback:
Mock<IFoo> mockFoo = new Mock<IFoo>();
mockFoo.Setup(f => f.Bar())
.Callback(() => Thread.Sleep(1000))
.Returns("test");
string result = mockFoo.Object.Bar(); // will take 1 second to return
Assert.AreEqual("test", result);
I've tried that in LinqPad and if you adjust the Thread.Sleep() the execution time varies accordingly.
When you setup your mock you can tell the thread to sleep in the return func:
Mock<IMyService> myService = new Mock<IMyService>();
myService.Setup(x => x.GetResultDelayed()).Returns(() => {
Thread.Sleep(100);
return "result";
});
If running asynchronous code, Moq has the ability to delay the response with the second parameter via a TimeSpan
mockFooService
.Setup(m => m.GetFooAsync())
.ReturnsAsync(new Foo(), TimeSpan.FromMilliseconds(500)); // Delay return for 500 milliseconds.
If you need to specify a different delay each time the method is called, you can use .SetupSequence like
mockFooService
.SetupSequence(m => m.GetFooAsync())
.Returns(new Foo())
.Returns(Task.Run(async () =>
{
await Task.Delay(500) // Delay return for 500 milliseconds.
return new Foo();
})
I could not get Moq version to work, so I ended up making something like this:
a small example using WaitHandle:
[TestFixture]
public class EventWaitHandleTests
{
class Worker {
private volatile bool _shouldStop;
public EventWaitHandle WaitHandleExternal;
public void DoWork ()
{
while (!_shouldStop)
{
Console.WriteLine("worker thread: working...");
Thread.Sleep(1000);
WaitHandleExternal.Set();
}
}
public void RequestStop()
{
_shouldStop = true;
}
}
[Test]
public void WaitForHandleEventTest()
{
EventWaitHandle _waitHandle = new AutoResetEvent (false); // is signaled value change to true
// start a thread which will after a small time set an event
Worker workerObject = new Worker ();
workerObject.WaitHandleExternal = _waitHandle;
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start();
Console.WriteLine ("Waiting...");
_waitHandle.WaitOne(); // Wait for notification
Console.WriteLine ("Notified");
// Stop the worker thread.
workerObject.RequestStop();
}
}
I like and voted for serup's solution. My answer is a version of his converted for use as a library.
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// support halting a workflow and waiting for a finish request
/// </summary>
public class MockWorker
{
private readonly DateTime? _end;
private volatile bool _shouldStop;
/// <summary>
/// Create a worker object
/// </summary>
/// <param name="timeoutInMilliseconds">How long before DoWork will timeout. default - Null will not timeout.</param>
public MockWorker(int? timeoutInMilliseconds = null)
{
if (timeoutInMilliseconds.HasValue)
_end = DateTime.Now.AddMilliseconds(timeoutInMilliseconds.Value);
}
/// <summary>
/// Instruct DoWork to complete
/// </summary>
public void RequestStop()
{
_shouldStop = true;
}
/// <summary>
/// Do work async will run until either timeoutInMilliseconds is exceeded or RequestStop is called.
/// </summary>
public async Task DoWorkAsync()
{
while (!_shouldStop)
{
await Task.Delay(100);
if (_end.HasValue && _end.Value < DateTime.Now)
throw new AssertFailedException("Timeout");
}
}
/// <summary>
/// Do work async will run until either timeoutInMilliseconds is exceeded or RequestStop is called.
/// </summary>
/// <typeparam name="T">Type of value to return</typeparam>
/// <param name="valueToReturn">The value to be returned</param>
/// <returns>valueToReturn</returns>
public async Task<T> DoWorkAsync<T>(T valueToReturn)
{
await DoWorkAsync();
return valueToReturn;
}
}
I had a similiar situation, but with an Async method. What worked for me was to do the following:
mock_object.Setup(scheduler => scheduler.MakeJobAsync())
.Returns(Task.Run(()=> { Thread.Sleep(50000); return Guid.NewGuid().ToString(); }));

Get async callback value in unittest debug

I got UnitTest method which calls method with callback
[Test]
public void GetUserAsyncTest()
{
User result;
_restTest.GetUserAsync((user) =>
{
result = user;
});
Assert.AreEqual("xy", result.Email);
}
This is my method signature
/// <summary>
/// Retrieve the User details for the currently authenticated User
/// </summary>
/// <param name="callback">Method to call upon successful completion</param>
public void GetUserAsync(Action<User> callback)
How can I test this and get value from callback? Currently my result is always null which is logical.
Use an event to wait until the async method has finished:
[Test]
public void GetUserAsyncTest()
{
//Action<User> user = null;
User result;
ManualResetEvent waitEvent = new ManualResetEvent(false);
_restTest.GetUserAsync((user) =>
{
result = user;
waitEvent.Set();
});
waitEvent.WaitOne();
Assert.AreEqual("xy", result.Email);
}
Also changed user.Email to result.Email. Suspected you want to check the result variable.

Categories