Find an attribute on a parent method in a deep async call - c#

I am looking for a way to "add some context" to my methods for debugging purposes. Even though using StackTrace works fine with sync code, when going async, things naturally stop working. I fully understand why but I can't find a good way to solve this.
[Scope("method 1")]
private async Task Method1()
{
// ... other awaited calls
await DoWork();
}
[Scope("method 2")]
private async Task Method2()
{
// ... other awaited calls
await DoWork();
}
private async Task DoWork()
{
// Get ScopeAttribute from parent method
var description = new StackTrace()
.GetFrames()
?.SelectMany(f => f.GetMethod().GetCustomAttributes(typeof(ScopeAttribute), false))
.Cast<ScopeAttribute>()
.FirstOrDefault()?.Description;
}
How to get to ScopeAttribute decorated on parent methods? In a sync world the above would just work. In an async world, the stack trace is lost. Any ideas?
The solution does not necessarily have to involve using attributes.

If I understand your use-case correctly, what you're looking for is AsyncLocal:
private static AsyncLocal<string> _scope = new AsyncLocal<string>();
private async Task Method1()
{
_scope.Value = "method 1";
// ... other awaited calls
await DoWork();
}
private async Task Method2()
{
_scope.Value = "method 2";
// ... other awaited calls
await DoWork();
}
private async Task DoWork()
{
Console.WriteLine(_scope.Value);
}

Related

How to compose a unit test if a task is followed by task.start()?

In program, we have:
var task1 = some task;
task1.start()
If mock the task result in unit test, the result is returned by mock mechanism immediately before calling the start(), and raise "Start may not be called on a task that has completed" exception.
How to address that issue and compose a valid unit test?
Paste a simplified sample code here for clarity, which produces the above exception:
namespace TestTaskStart
{
public class TestMethods : ITestMethods
{
public async Task<int> AlwaysReturnOne(int number)
{
return await Task.FromResult(1);
}
}
}
namespace TestTaskStart {
public class TestInvoker
{
private ITestMethods testMethods;
public TestInvoker(ITestMethods testMethods)
{
this.testMethods = testMethods;
}
public async Task<int> GetANumberWrapperTask(int number)
{
// just an exmple of one tasks to be called
var task = this.testMethods.AlwaysReturnOne(number);
task.Start();
Task.WaitAll(task);
return task.Result;
}
}
}
namespace TestTaskStart {
[TestClass]
public class UnitTests
{
ITestMethods numberGetter;
TestInvoker testInvoker;
[TestInitialize]
public void Setup()
{
this.numberGetter = Substitute.For<ITestMethods>();
this.testInvoker = new TestInvoker(this.numberGetter);
}
[TestMethod]
public void TestGetANumberWrapper()
{
this.MockAlwaysReturnOneResult();
var result = testInvoker.GetANumberWrapperTask(5).Result;
}
private void MockAlwaysReturnOneResult()
{
this.numberGetter.AlwaysReturnOne(Arg.Any<int>()).Returns(1);
}
}
}
The Task.Start method can only be called on "cold" tasks, in other words on tasks that have not been started yet. Such tasks can only be created with the Task constructor. Tasks created by invoking asynchronous methods implemented with the async keyword are "hot", in other words they are already started upon creation. From the docs:
Exceptions
InvalidOperationException
The Task is not in a valid state to be started. It may have already been started, executed, or canceled, or it may have been created in a manner that doesn't support direct scheduling.
This is also a good reading: A Tour of Task, Part 10: Promise Tasks

Task based behavior is blocking the parent thread [duplicate]

This question already has an answer here:
Async Await Running Synchronously
(1 answer)
Closed 2 years ago.
Assuming that I need to have some concurrent processes and I would like to forget about the old Thread class and stick to Task with an async pattern. Given the following codes:
// Scenario 1
public async Task Accept() {
while(true) {
Connection con = await connection.AcceptAsync();
await HandleConnection(con);
}
}
public Task HandleConnection(Connection con) {
// Long Running Logic
return Task.CompletedTask;
}
// Scenario 2
public async Task Accept() {
while(true) {
Connection con = await connection.AcceptAsync();
HandleConnection(con); // Produces a warning
}
}
public Task HandleConnection(Connection con) {
// Long Running Logic
return Task.CompletedTask;
}
Both these approaches fail and I cannot handle the connection concurrently. For example when the first connection is accepted then I cannot accept the second connection until the HandleConnection method finishes its job. To solve this problem I can do the following:
public async Task Accept() {
while(true) {
Connection con = await connection.AcceptAsync();
HandleConnection(con); // Produces a warning
}
}
public Task HandleConnection(Connection con) {
return Task.Run(()=> {
//Long Running Logic
});
}
Now I am able to handle multiple connections but this behavior raises a few questions:
1- I heard that the await keyword in contrast with wait is non-blocking and in fact the whole async pattern in non-blocking as well. But in this situation it is actually blocking the parent thread.
2- When a method is async, a Task resembling that method must be generated as a result so that it can be awaited, is that true? if that is true then why is it blocking? else how does the await mechanism work?
3- If I don't await the Task, I get a compile time warning which says I have to await the task, but if I use it then the parent thread is blocked.
To ditch the warning I can do HandleConnection(con).Wait(0) but isn't that a bad practice?
I played with this issue and tested that thoroughly. The conclusion is that in a method that returns a Task, if you do not actually return a Task or instead await or return Task.CompletedTask, that method will still run on the Parent thread and will surely block it. You can only benefit from multithreading if you actually raise a Task as I represented in my question.
To prove this, the following is blocking:
public static void Main(String[] args) {
test();
Console.WriteLine("Hello World");
}
private static Task test() {
while(true) {}
return Task.CompletedTask;
}
In this code test will block the main thread.
If you make the test async, it will still block the main thread.
public static void Main(String[] args) {
test();
Console.WriteLine("Hello World");
}
private static async Task test() {
while(true) {}
}
This is while the following does not block the main thread:
public static async Task test() {
await Task.Run(()=> {
while(true) {}
});
}
However, if you don't want to await the Task you should raise the Task in void method:
public void Method() {
Task.Run(()=> ());
}
This is much better than Task.Wait(0).

Async method deadlocks with TestScheduler in ReactiveUI

I'm trying to use the reactiveui test scheduler with an async method in a test.
The test hangs when the async call is awaited.
The root cause seems to be a command that's awaited in the async method.
[Fact]
public async Task Test()
=> await new TestScheduler().With(async scheduler =>
{
await SomeAsyncMethod();
// *** execution never gets here
Debugger.Break();
});
private async Task SomeAsyncMethod()
{
var command = ReactiveCommand.CreateFromTask(async () =>
{
await Task.Delay(100);
});
// *** this hangs
await command.Execute();
}
How can I do an async call in combination with the test scheduler that does not deadlock?
I'm using reactiveui 9.4.1
EDIT:
I've tried the WithAsync() method as suggested in Funks answer, but the behaviour is the same.
How can I do an async call in combination with the test scheduler?
In short
command.Execute() is a cold observable. You need to subscribe to it, instead of using await.
Given your interest in TestScheduler, I take it you want to test something involving time. However, from the When should I care about scheduling section:
threads created via "new Thread()" or "Task.Run" can't be controlled in a unit test.
So, if you want to check, for example, if your Task completes within 100ms, you're going to have to wait until the async method completes. To be sure, that's not the kind of test TestScheduler is meant for.
The somewhat longer version
The purpose of TestScheduler is to verify workflows by putting things in motion and verifying state at certain points in time. As we can only manipulate time on a TestScheduler, you'd typically prefer not to wait on real async code to complete, given there's no way to fast forward actual computations or I/O. Remember, it's about verifying workflows: vm.A has new value at 20ms, so vm.B should have new val at 120ms,...
So how can you test the SUT?
1\ You could mock the async method using scheduler.CreateColdObservable
public class ViewModelTests
{
[Fact]
public void Test()
{
string observed = "";
new TestScheduler().With(scheduler =>
{
var observable = scheduler.CreateColdObservable(
scheduler.OnNextAt(100, "Done"));
observable.Subscribe(value => observed = value);
Assert.Equal("", observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", observed);
scheduler.AdvanceByMs(1);
Assert.Equal("Done", observed);
});
}
}
Here we basically replaced command.Execute() with var observable created on scheduler.
It's clear the example above is rather simple, but with several observables notifying each other this kind of test can provide valuable insights, as well as a safety net while refactoring.
Ref:
Answer by Paul Betts
Control Time with the TestScheduler
2\ You could reference the IScheduler explicitly
a) Using the schedulers provided by RxApp
public class MyViewModel : ReactiveObject
{
public string Observed { get; set; }
public MyViewModel()
{
Observed = "";
this.MyCommand = ReactiveCommand
.CreateFromTask(SomeAsyncMethod);
}
public ReactiveCommand<Unit, Unit> MyCommand { get; }
private async Task SomeAsyncMethod()
{
await RxApp.TaskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));
Observed = "Done";
}
}
public class ViewModelTests
{
[Fact]
public void Test()
{
new TestScheduler().With(scheduler =>
{
var vm = new MyViewModel();
vm.MyCommand.Execute().Subscribe();
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(1);
Assert.Equal("Done", vm.Observed);
});
}
}
Note
CreateFromTask creates a ReactiveCommand with asynchronous execution logic. There's no need to define the Test method as async or await the TestScheduler.
Within the With extension method's scope RxApp.TaskpoolScheduler = RxApp.MainThreadScheduler = the new TestScheduler().
b) Managing your own schedulers through constructor injection
public class MyViewModel : ReactiveObject
{
private readonly IScheduler _taskpoolScheduler;
public string Observed { get; set; }
public MyViewModel(IScheduler scheduler)
{
_taskpoolScheduler = scheduler;
Observed = "";
this.MyCommand = ReactiveCommand
.CreateFromTask(SomeAsyncMethod);
}
public ReactiveCommand<Unit, Unit> MyCommand { get; }
private async Task SomeAsyncMethod()
{
await _taskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));
Observed = "Done";
}
}
public class ViewModelTests
{
[Fact]
public void Test()
{
new TestScheduler().With(scheduler =>
{
var vm = new MyViewModel(scheduler); ;
vm.MyCommand.Execute().Subscribe();
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(0);
Assert.Equal("Done", vm.Observed);
});
}
}
Ref:
Kent Boogaert's Answer
Testing Rx code - ISchedulerProvider
Let's close ranks with another quote from Haacked:
Unfortunately, and this next point is important, the TestScheduler doesn’t extend into real life, so your shenanigans are limited to your asynchronous Reactive code. Thus, if you call Thread.Sleep(1000) in your test, that thread will really be blocked for a second. But as far as the test scheduler is concerned, no time has passed.
Have you tried to use ConfigureAwait(false) when calling nested method?
[Fact]
public async Task Test()
=> await new TestScheduler().With(async scheduler =>
{
// this hangs
await SomeAsyncMethod().ConfigureAwait(false);
// ***** execution will never get to here
Debugger.Break();
}
Please try using .ConfigureAwait(false) on all your async methods.
This will provide you non-blocking behavior.
[Fact]
public async Task Test()
=> await new TestScheduler().With(async scheduler =>
{
await SomeAsyncMethod().ConfigureAwait(false);
// *** execution never gets here
Debugger.Break();
}).ConfigureAwait(false);
private async Task SomeAsyncMethod()
{
var command = ReactiveCommand.CreateFromTask(async () =>
{
await Task.Delay(100).ConfigureAwait(false);
}).ConfigureAwait(false);
// *** this hangs
await command.Execute();
}
Another way to test whether the problem is related with ConfigureAwait is to port your project to Asp.Net Core and test it there.
Asp.net core does not need to use ConfigureAwait to prevent this blocking issue.
Check this for Reference

How can I get the result of my work in a Task

I need to do a work in a Task (infinite loop for monitoring) but how can I get the result of this work?
My logic to do this stuff i wrong? This is a scope problem I think.
There is an example simplified:
The variable is "first" and I want "edit"
namespace my{
public class Program{
public static void Main(string[] args){
Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
Console.WriteLine(p.getVar());// result="first"
}
}
public class Logic{
public string test = "first";
public void process(){
while(true){
//If condition here
this.test = "edit";
}
}
public String getVar(){
return this.test;
}
}
}
It can be done using custom event. In your case it can be something like:
public event Action<string> OnValueChanged;
Then attach to it
p.OnValueChanged += (newValue) => Console.WriteLine(newValue);
And do not forget to fire it
this.test = "edit";
OnValueChanged?.Invoke(this.test);
Tasks aren't threads, they don't need a .Start call to start them. All examples and tutorials show the use of Task.Run or Task.StartNew for a reason - tasks are a promise that a function will execute at some point in the future and produce a result. They will run on threads pulled from a ThreadPool when a Task Scheduler decides they should. Creating cold tasks and calling .Start doesn't guarantee they will start, it simply makes the code a lot more difficult to read.
In the simplest case, polling eg a remote HTTP endpoint could be as simple as :
public static async Task Main()
{
var client=new HttpClient(serverUrl);
while(true)
{
var response=await client.GetAsync(relativeServiceUrl);
if(!response.IsSuccessStatusCode)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}
There's no need to start a new Task because GetAsync is asynchronous. WCF and ADO.NET also provide asynchronous execution methods.
If there's no asynchronous method to call, or if we need to perform some heavey work before the async call, we can use Task.Run to start a method in parallel and await for it to finish:
public bool CheckThatService(string serviceUrl)
{
....
}
public static async Task Main()
{
var url="...";
//...
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}
What if we want to test multiple systems in parallel? We can start multiple tasks in parallel, await all of them to complete and check their results:
public static async Task Main()
{
var urls=new[]{"...","..."};
//...
while(true)
{
var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses)
{
///Check the value, due something
}
await Task.Delay(1000);
}
}
Task.WhenAll returns an array with the results in the order the tasks were created. This allows checking the index to find the original URL. A better idea would be to return the result and url together, eg using tuples :
public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
return (true,url);
}
The code wouldn't change a lot:
var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses.Where(resp=>!resp.ok))
{
///Check the value, due something
}
What if we wanted to store the results from all the calls? We can't use a List or Queue because they aren't thread safe. We can use a ConcurrentQueue instead:
ConcurrentQueue<string> _results=new ConcurrentQueue<string>();
public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
_results.Enqueue(someresult);
return (true,url);
}
If we want to report progress regularly we can use IProgress<T> as shown in Enabling Progress and Cancellation in Async APIs.
We could put all the monitoring code in a separate method/class that accepts an IProgress< T> parameter with a progress object that can report success, error messages and the URL that caused them, eg :
class MonitorDTO
{
public string Url{get;set;}
public bool Success{get;set;}
public string Message{get;set;}
public MonitorDTO(string ulr,bool success,string msg)
{
//...
}
}
class MyMonitor
{
string[] _urls=url;
public MyMonitor(string[] urls)
{
_urls=url;
}
public Task Run(IProgress<MonitorDTO> progress)
{
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
_progress.Report(new MonitorDTO(ok,url,"some message");
}
await Task.Delay(1000);
}
}
}
This class could be used in this way:
public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
await monitor.Run(progress);
}
Enabling Progress and Cancellation in Async APIs shows how to use the CancellationTokenSource to implement another important part of a monitoring class - cancelling it. The monitoring method could check the status of a cancellation token periodically and stop monitoring when it's raised:
public Task Run(IProgress<MonitorDTO> progress,CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
//...
}
}
public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
var cts = new CancellationTokenSource();
//Not awaiting yet!
var monitorTask=monitor.Run(progress,cts.Token);
//Keep running until the first keypress
Console.ReadKey();
//Cancel and wait for the monitoring class to gracefully stop
cts.Cancel();
await monitorTask;
In this case the loop will exit when the CancellationToken is raised. By not awaiting on MyMonitor.Run() we can keep working on the main thread until an event occurs that signals monitoring should stop.
The getVar method is executed before the process method.
Make sure that you wait until your task is finished before you call the getVar method.
Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
t.Wait(); // Add this line!
Console.WriteLine(p.getVar());
If you want to learn more about the Wait method, please check this link.

Why is await exiting?

Given the following code:
public async Task Send() // part of Sender class
{
// sync code
}
// //
private async Task HandleMessage()
{
// await sender.Send(); // exits HandleMessage immediately
sender.Send().Wait(); // works as expected, waiting to complete
DoOtherStuff(); // doesn't get hit with await
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
public void RunRecurringTask(Action action, RecurringTaskRunResult result)
{
action();
result.DoStuff();
}
I thought that await tells the thread to come back when the awaited thing is complete, but it looks like for some reason that's not happening: the remaining code is never hit and everything just... stops. What could be causing this?
This is a console application in an Azure WebJob, for what it's worth. When Wait is used, I get the expected results, however with await, the job just completes.
You should never do async void unless you are writing a event handler. A Action with the async modifier is a async void method. You need to make the argument a Func<Task> and then do await action() in your RunRecurringTask
private async Task HandleMessage()
{
await sender.Send();
DoOtherStuff();
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
//You also could do
//RunRecurringTask(() => HandleMessage(), result);
public async Task RunRecurringTask(Func<Task> action, RecurringTaskRunResult result)
{
await action();
result.DoStuff();
}
If you had other methods that where not marked with async you will need to change all of them up the call stack till you get to the entry point from the SDK. The SDK understands how to handle functions with a async Task return type since the 0.4.0-beta version.

Categories

Resources