languageext Either.Map/Bind with a Task in the Right position - c#

I am using the toolkit languageext package for C# and am running into a problem with the Either class when the Right value is some kind of Task. For some reason this is causing a hang:
var res = repo.GetAccountWithID(accountID)
.Map(c => filesServiceCustomer.Initialize(c))
.Bind(t => t.Result);
Here, GetAccountWithID returns an Either<Exception, Account> and the Initialize method take an Account and returns a Task<Either<Exception, bool>>. However, it would appear that either the Map or Bind calls are hanging.
Does anyone have any idea what might be causing this or what to do about it?

(I'm the author of the language-ext project). There's no fundamental reason for your expression to hang, other than if the Task itself is blocking - Map and Bind are trivial functions that don't do anything particularly clever, and definitely don't do any synchronisation or anything like that. I just added this code to the unit tests in lang-ext, and it returns fine:
public class Account : NewType<Account, Unit>
{
public Account(Unit _) : base(unit) { }
}
Either<Exception, Account> GetAccountWithID(int accountId) =>
Account.New(unit);
Task<Either<Exception, bool>> Initialize(Account c) =>
Task.FromResult(Right<Exception, bool>(true));
[Fact]
public void StackOverflowQuestion()
{
int accountID = 0;
var res = GetAccountWithID(accountID)
.Map(c => Initialize(c))
.Bind(t => t.Result);
}
One thing it's worth mentioning is that it's not great practice to call .Result on a task. You can definitely leverage other features in language-ext to make this work better for you:
For example:
var task = from c in GetAccountWithID(accountID).AsTask()
from r in Initialize(c)
select r;
AsTask lifts the Either<Exception, Account> into a Task<Either<Exception, Account>>, which then means it's usable in a LINQ expression with Initialize (which also returns a Task).
If you're fundamentally opposed to the LINQ syntax, then you can do:
var task = GetAccountWithID(accountID).AsTask().BindT(Initialize);
task is then a Task<Either<Exception, bool>> which you can await:
var res = (await task).IfLeft(false);
Another trick (if you're using version 2.0.*) is to use Sequence which flips the inner and outer monads:
var res = task.Sequence();
That will turn the Task<Either<Exception, bool>> into a Either<Exception, Task<bool>> which you can match on. Obviously it depends on your use-case as to what's most appropriate.

Most probably, your environment has a synchronization context and calling Result or Wait will almost always deadlock.
I don't know what that library does, but this will probably work:
var res = (await repo.GetAccountWithID(accountID)
.Map(c => filesServiceCustomer.Initialize(c)))
.Bind(t => t);

A little more context here on what I found and what is happening. I managed to 'fix' the problem though I am not sure exactly what was causing it or why the fix is needed.
First of all this is running in an Azure API app service. Not sure if that makes a difference or not but it is included for completeness.
Inside the Initialize function there were two lines at the end that look like this:
rootDir = someShare.GetRoodDirectoryReference();
...
dir1 = rootDir.GetDirectoryReference(dir1Name);
await dir1.CreateIfNotExistsAsync();
dir2 = rootDir.GetDirectoryReference(dir2Name);
await dir2.CreateIfNotExistsAsync();
The code was hanging on the await of the first CreateIfNotExistAsync() call (whichever was in that position, it didn't matter). However, I changed this to:
dir1 = rootDir.GetDirectoryReference(dir1Name);
dir2 = rootDir.GetDirectoryReference(dir2Name);
Task<bool> tasks = {
Task.Run(dir1.CreateIfNotExistAsync),
Task.Run(dir2.CreateIfNotExistAsync),
};
Task.WaitAll(tasks);
And, like magic, no more hang!
Now my calling code works as expected. I don't know why this fix is needed. The only thing I can think of is that the continuation created by the await statements were somehow causing a problem. I don't really want to dig into the guts of the compiler-generated continuations if I don't have to, though.

Related

How to write async methods with await, but make them as short as with Task.Run?

After browsing different await vs Task.Run questions on SO my takeaway is that await is better for I/O operations and Task.Run for CPU-intensive operations. However the code with await seems to always be longer than Task.Run. Below is an example method of how I am creating a context object with await in my app:
public async AppContext CreateAppContext()
{
var context = new AppContext();
var customersTask = _dataAccess.GetCustomers();
var usersTask = _dataAccess.GetUsers();
var reportsTask = _dataAccess.GetReports();
context.Customers = await customersTask;
context.Users = await usersTask;
context.Reports = await reportsTask;
return context;
}
If I was to rewrite this with Task.Run I could do
public async AppContext CreateAppContext()
{
var context = new AppContext();
await Task.WhenAll(new[]
{
Task.Run(async () => { context.Customers = await _dataAccess.GetCustomers(); }),
Task.Run(async () => { context.Users = await _dataAccess.GetUsers(); }),
Task.Run(async () => { context.Reports = await _dataAccess.GetReports(); });
})
return context;
}
The difference is not major when I create an object with 3 properties but I have objects where I need to initialize 20+ properties in this manner which makes the await code a lot longer (nearly double) than Task.Run. Is there a way for me to initialize the object using await with code that is not a lot longer than what I can do with Task.Run?
Personally, I prefer an asynchronous factory pattern over that kind of code; but if you need to do concurrent asynchronous work like that multiple times, then you'll probably want to write your own helper method.
The BCL-provided WhenAll works best when either all tasks have no results, or when all tasks have the same type of result. One fairly common approach to help WhenAll work with different types of tasks is to return a tuple of results, which can then be deconstructed into different variables if desired. E.g.,
public static class TaskEx
{
public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(Task<T1> task1, Task<T2> task2, Task<T3> task3)
{
await Task.WhenAll(task1, task2, task3);
return (await task1, await task2, await task3);
}
}
// Usage
public async AppContext CreateAppContext()
{
var context = new AppContext();
(context.Customers, context.Users, conext.Reports) =
await TaskEx.WhenAll(
_dataAccess.GetCustomers(),
_dataAccess.GetUsers(),
_dataAccess.GetReports());
return context;
}
You can even define a tuple GetAwaiter extension method if you want, to make it more implicit:
// Usage
public async AppContext CreateAppContext()
{
var context = new AppContext();
(context.Customers, context.Users, conext.Reports) =
await (
_dataAccess.GetCustomers(),
_dataAccess.GetUsers(),
_dataAccess.GetReports());
return context;
}
There are a couple of disadvantages to these approaches, though. First, you have to define as many overloads as you need. Second, the multiple-assignment code is not very nice; it's fine for 2 or 3 properties, but would get ugly (IMO) if done for much more than that.
So I think what you really want is a custom delegate form of WhenAll. Something like this should work:
public static class TaskEx
{
public static async Task WhenAll(params Func<Task>[] tasks)
{
return Task.WhenAll(tasks.Select(action => action()));
}
}
// Usage
public async AppContext CreateAppContext()
{
var context = new AppContext();
await TaskEx.WhenAll(
async () => context.Customers = await _dataAccess.GetCustomers(),
async () => context.Users = await _dataAccess.GetUsers(),
async () => conext.Reports = await _dataAccess.GetReports());
return context;
}
Since this solution avoids dealing with the different result types entirely, multiple overloads aren't needed.
If you really want to keep your general pattern (which I'd avoid - it would be much better to do all the work and then assign all the results at the same time; look into the return value of Task.WhenAll), all you need is a simple helper method:
static async Task Assign<T>(Action<T> assignment, Func<Task<T>> getValue)
=> assignment(await getValue());
Then you can use it like this:
await Task.WhenAll
(
Assign(i => context.Customers = i, _dataAccess.GetCustomers),
Assign(i => context.Users = i, _dataAccess.GetUsers),
Assign(i => context.Reports = i, _dataAccess.GetReports)
);
There's many other ways to make this even simpler, but this is the most equivalent to your Task.Run code without having to involve another thread indirection just to do an assignment. It also avoids the very common mistake when you happen to use the wrong Task.Run overload and get a race condition (as Task.Run returns immediately instead of waiting for the result).
Also, you misunderstood the "await vs. Task.Run" thing. There's actually not that much difference between await and Task.Run in your code - mainly, it forces a thread switch (and a few other subtle things). The argument is against using Task.Run to run synchronous code; that wastes a thread waiting for a thing to complete, your code doesn't.
Do keep in mind that WhenAll comes with its own complications, though. While it does mean you don't have to worry about some of the tasks ending up unobserved (and not waited on!), it also means you have to completely rethink your exception handling, since you're going to get an AggregateException rather than anything more specific. If you're relying on error handling based on identifying exceptions, you need to be very careful. Usually, you don't want AggregateException to leak out of methods - it's very difficult to handle in a global manner; the only method that knows the possibilities of what can happen is the method that calls the WhenAll. Hopefully.
It's definitely a good idea to run parallel operations like this in a way that cannot produce dangerous and confusing side-effects. In your code, you either get a consistent object returned, or you get nothing - that's exactly the right approach. Be wary of this approach leaking into other contexts - it can get really hard to debug issues where randomly half of the operations succeed and other half fails :)
Is there a way for me to initialize the object using await with code that is not a lot longer than what I can do with Task.Run?
If you want to run all tasks in parallel - in short no, you cant shorten number of lines. Also note that those two snippets are not fully functionally equivalent - see Why should I prefer single await Task.WhenAll over multiple awaits.
You can simplify (and maybe even improve performance a bit) your Task.WhenAll approach by introducing a method which will await and assign. Something along these lines:
public async AppContext CreateAppContext()
{
var context = new AppContext();
await Task.WhenAll(
AwaitAndAssign(val => context.Customers = val, _dataAccess.GetCustomers()),
AwaitAndAssign(val => context.Users = val, _dataAccess.Users()),
AwaitAndAssign(val => context.Reports = val, _dataAccess.GetReports())
);
return context;
async Task AwaitAndAssign<T>(Action<T> assign, Task<T> valueTask) =>
assign(await valueTask);
}

Delay Returns with NSubstitute

I have an interface IDiscosClient, for testing/demo purposes while I'm developing the app, I want a mock to return a new model when the .GetSingle<T>() method is called with a random delay of between 1 and 5 seconds. This is mostly so I can see that all of my various loading spinner components and whatnot work.
So, I thought I'd be able to do something like this:
Fixture fixture = new();
fixture.Customize(new DiscosModelFixtureCustomizationNoLinks());
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
DiscosObject obj = fixture.Create<DiscosObject>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
return client;
});
However, while there seems to be a delay when I first call the method, once this has resolved, it just seems to return the completed task with the same model in it every time I call it for that IDiscosClient instance.
Is there a simple enough way to accomplish this?
So the issue is that the code above only creates a fresh Task the first time and then returns the same one (which has already completed) each subsequent time.
To fix this, we can either change the code above to:
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(_ => Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
Or, for legibilities sake, we can extract it into a method and make the whole code block:
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(GetDiscosObject);
return client;
});
async Task<DiscosObject> GetDiscosObject(CallInfo _)
{
await Task.Delay(Random.Shared.Next(1000, 5000));
return fixture.Create<DiscosObject>();
}

Scatter gather with async await

I have a situation where I am interested in the first successful response from an array of services that each support the method
Task<Try<SearchResponse>> PerformSearch(SearchRequest request);
The Try class is a container for a Good/Bad result (like error Monad)
The call to the list of services currently is this
var searchResponses = await Task.WhenAll(
_searchServices.Select(s => s.PerformSearch(request)));
return searchResponses.FirstOrBad(sr=>sr.IsGood);
Where FirstOrBad is an extension method that finds the first good result or returns a composite Bad Try with a concatenation of all the errors.
As far as I understand the problem with this is that due to the WhenAll the time to find the first good result is limited by the slowest response.
I want to continue execution as soon as I receive the first positive result but not the first (2nd ... etc) result if it is not successful, but also continue execution if all results return unsuccessfully, reporting the lack of success.
I would have thought this is a common problem but have found little when searching for examples. It maybe known by some other term than scatter gather.
Something like this should work for you
public static async Task<Try<T>> FirstOrBad<T>(this IEnumerable<Task<Try<T>>> tasks, Func<Try<T>, bool> predicate)
{
var taskList = tasks.ToList();
var completed = new List<Task<Try<T>>>();
Task<Try<T>> completedTask;
do
{
completedTask = await Task.WhenAny(taskList);
completed.Add(completedTask);
taskList.Remove(completedTask);
} while (!predicate(await completedTask) && taskList.Any());
return !predicate(await completedTask) ? new Try<T>(completed.ToString(",")) : await completedTask;
}
Adapter from this answer TPL wait for task to complete with a specific return value

Async await in linq select

I need to modify an existing program and it contains following code:
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
But this seems very weird to me, first of all the use of async and awaitin the select. According to this answer by Stephen Cleary I should be able to drop those.
Then the second Select which selects the result. Doesn't this mean the task isn't async at all and is performed synchronously (so much effort for nothing), or will the task be performed asynchronously and when it's done the rest of the query is executed?
Should I write the above code like following according to another answer by Stephen Cleary:
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
and is it completely the same like this?
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
While i'm working on this project I'd like to change the first code sample but I'm not too keen on changing (apparantly working) async code. Maybe I'm just worrying for nothing and all 3 code samples do exactly the same thing?
ProcessEventsAsync looks like this:
async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
But this seems very weird to me, first of all the use of async and await in the select. According to this answer by Stephen Cleary I should be able to drop those.
The call to Select is valid. These two lines are essentially identical:
events.Select(async ev => await ProcessEventAsync(ev))
events.Select(ev => ProcessEventAsync(ev))
(There's a minor difference regarding how a synchronous exception would be thrown from ProcessEventAsync, but in the context of this code it doesn't matter at all.)
Then the second Select which selects the result. Doesn't this mean the task isn't async at all and is performed synchronously (so much effort for nothing), or will the task be performed asynchronously and when it's done the rest of the query is executed?
It means that the query is blocking. So it is not really asynchronous.
Breaking it down:
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
will first start an asynchronous operation for each event. Then this line:
.Select(t => t.Result)
will wait for those operations to complete one at a time (first it waits for the first event's operation, then the next, then the next, etc).
This is the part I don't care for, because it blocks and also would wrap any exceptions in AggregateException.
and is it completely the same like this?
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
Yes, those two examples are equivalent. They both start all asynchronous operations (events.Select(...)), then asynchronously wait for all the operations to complete in any order (await Task.WhenAll(...)), then proceed with the rest of the work (Where...).
Both of these examples are different from the original code. The original code is blocking and will wrap exceptions in AggregateException.
I used this code:
public static async Task<IEnumerable<TResult>> SelectAsync<TSource,TResult>(
this IEnumerable<TSource> source, Func<TSource, Task<TResult>> method)
{
return await Task.WhenAll(source.Select(async s => await method(s)));
}
like this:
var result = await sourceEnumerable.SelectAsync(async s=>await someFunction(s,other params));
Edit:
Some people have raised the issue of concurrency, like when you are accessing a database and you can't run two tasks at the same time. So here is a more complex version that also allows for a specific concurrency level:
public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, Task<TResult>> method,
int concurrency = int.MaxValue)
{
var semaphore = new SemaphoreSlim(concurrency);
try
{
return await Task.WhenAll(source.Select(async s =>
{
try
{
await semaphore.WaitAsync();
return await method(s);
}
finally
{
semaphore.Release();
}
}));
} finally
{
semaphore.Dispose();
}
}
Without a parameter it behaves exactly as the simpler version above. With a parameter of 1 it will execute all tasks sequentially:
var result = await sourceEnumerable.SelectAsync(async s=>await someFunction(s,other params),1);
Note: Executing the tasks sequentially doesn't mean the execution will stop on error!
Just like with a larger value for concurrency or no parameter specified, all the tasks will be executed and if any of them fail, the resulting AggregateException will contain the thrown exceptions.
If you want to execute tasks one after the other and fail at the first one, try another solution, like the one suggested by xhafan (https://stackoverflow.com/a/64363463/379279)
Existing code is working, but is blocking the thread.
.Select(async ev => await ProcessEventAsync(ev))
creates a new Task for every event, but
.Select(t => t.Result)
blocks the thread waiting for each new task to end.
In the other hand your code produce the same result but keeps asynchronous.
Just one comment on your first code. This line
var tasks = await Task.WhenAll(events...
will produce a single Task<TResult[]> so the variable should be named in singular.
Finally your last code make the same but is more succinct.
For reference: Task.Wait / Task.WhenAll
I prefer this as an extension method:
public static async Task<IEnumerable<T>> WhenAll<T>(this IEnumerable<Task<T>> tasks)
{
return await Task.WhenAll(tasks);
}
So that it is usable with method chaining:
var inputs = await events
.Select(async ev => await ProcessEventAsync(ev))
.WhenAll()
I have the same problem as #KTCheek in that I need it to execute sequentially. However I figured I would try using IAsyncEnumerable (introduced in .NET Core 3) and await foreach (introduced in C# 8). Here's what I have come up with:
public static class IEnumerableExtensions {
public static async IAsyncEnumerable<TResult> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector) {
foreach (var item in source) {
yield return await selector(item);
}
}
}
public static class IAsyncEnumerableExtensions {
public static async Task<List<TSource>> ToListAsync<TSource>(this IAsyncEnumerable<TSource> source) {
var list = new List<TSource>();
await foreach (var item in source) {
list.Add(item);
}
return list;
}
}
This can be consumed by saying:
var inputs = await events.SelectAsync(ev => ProcessEventAsync(ev)).ToListAsync();
Update: Alternatively you can add a reference to System.Linq.Async and then you can say:
var inputs = await events
.ToAsyncEnumerable()
.SelectAwait(async ev => await ProcessEventAsync(ev))
.ToListAsync();
With current methods available in Linq it looks quite ugly:
var tasks = items.Select(
async item => new
{
Item = item,
IsValid = await IsValid(item)
});
var tuples = await Task.WhenAll(tasks);
var validItems = tuples
.Where(p => p.IsValid)
.Select(p => p.Item)
.ToList();
Hopefully following versions of .NET will come up with more elegant tooling to handle collections of tasks and tasks of collections.
I wanted to call Select(...) but ensure it ran in sequence because running in parallel would cause some other concurrency problems, so I ended up with this.
I cannot call .Result because it will block the UI thread.
public static class TaskExtensions
{
public static async Task<IEnumerable<TResult>> SelectInSequenceAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> asyncSelector)
{
var result = new List<TResult>();
foreach (var s in source)
{
result.Add(await asyncSelector(s));
}
return result;
}
}
Usage:
var inputs = events.SelectInSequenceAsync(ev => ProcessEventAsync(ev))
.Where(i => i != null)
.ToList();
I am aware that Task.WhenAll is the way to go when we can run in parallel.
"Just because you can doesn't mean you should."
You can probably use async/await in LINQ expressions such that it will behave exactly as you want it to, but will any other developer reading your code still understand its behavior and intent?
(In particular: Should the async operations be run in parallel or are they intentionally sequential? Did the original developer even think about it?)
This is also shown clearly by the question, which seems to have been asked by a developer trying to understand someone else's code, without knowing its intent. To make sure this does not happen again, it may be best to rewrite the LINQ expression as a loop statement, if possible.

How to use async countdown event instead of collecting tasks and awaiting on them?

I have the following code:
var tasks = await taskSeedSource
.Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem))
.ToList()
.ToTask();
if (tasks.Count == 0)
{
return;
}
if (tasks.Contains(null))
{
tasks = tasks.Where(t => t != null).ToArray();
if (tasks.Count == 0)
{
return;
}
}
await Task.WhenAll(tasks);
Where taskSeedSource is a Reactive Observable. It could be that this code have many problems, but I see at least two:
I am collecting tasks whereas I could do without it.
Somehow, the returned tasks list may contain nulls, even though GetPendingOrRunningTask is an async method and hence never returns null. I failed to understand why it happens, so I had to defend against it without understanding the cause of the problem.
I would like to use the AsyncCountdownEvent from the AsyncEx framework instead of collecting the tasks and then awaiting on them.
So, I can pass the countdown event to GetPendingOrRunningTask which will increment it immediately and signal before returning after awaiting for the completion of its internal logic. However, I do not understand how to integrate the countdown event into the monad (that is the Reactive jargon, isn't it?).
What is the right way to do it?
EDIT
Guys, let us forget about the mysterious nulls in the returned list. Suppose everything is green and the code is
var tasks = await taskSeedSource
.Select(taskSeed => GetPendingOrRunningTask(taskSeed, ...))
.ToList()
.ToTask();
await Task.WhenAll(tasks);
Now the question is how do I do it with the countdown event? So, suppose I have:
var c = new AsyncCountdownEvent(1);
and
async Task GetPendingOrRunningTask<T>(AsyncCountdownEvent c, T taskSeed, ...)
{
c.AddCount();
try
{
await ....
}
catch (Exception exc)
{
// The exception is handled
}
c.Signal();
}
My problem is that I no longer need the returned task. These tasks where collected and awaited to get the moment when all the work items are over, but now the countdown event can be used to indicate when the work is over.
My problem is that I am not sure how to integrate it into the Reactive chain. Essentially, the GetPendingOrRunningTask can be async void. And here I am stuck.
EDIT 2
Strange appearance of a null entry in the list of tasks
#Servy is correct that you need to solve the null Task problem at the source. Nobody wants to answer a question about how to workaround a problem that violates the contracts of a method that you've defined yourself and yet haven't provided the source for examination.
As for the issue about collecting tasks, it's easy to avoid with Merge if your method returns a generic Task<T>:
await taskSeedSource
.Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem))
.Where(task => task != null) // According to you, this shouldn't be necessary.
.Merge();
However, unfortunately there's no official Merge overload for the non-generic Task but that's easy enough to define:
public static IObservable<Unit> Merge(this IObservable<Task> sources)
{
return sources.Select(async source =>
{
await source.ConfigureAwait(false);
return Unit.Default;
})
.Merge();
}

Categories