-
Notifications
You must be signed in to change notification settings - Fork 933
[WIP] Async generator #588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
||
private async Task<IList<TRoot>> ListAsync() | ||
{ | ||
return await (criteria.ListAsync<TRoot>()).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maca88 I think we do not need to await here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the less we await, the better. Everywhere the task may just get passed along without awaiting, it should be passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, it seems that the "algorithm" to check if the returned type from the async method matches the method return type fails here. Will check and fix after I get home.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When returning the Task
without awaiting it, remove async
keyword.
Edit: Ok, I have missed completely the point, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot, @maca88
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Asked on SO how to implement this: http://stackoverflow.com/q/43792577/259946
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats great! I am really curious what will be the answser :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there would be no answers:(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This other question is similar.
In the case of an API having some not implemented members, for me it does not make much sens to delay the exception to the next await by putting it in a task. I would rather have it fail as early as possible.
(I would tend to do that for any cases indeed. If we fail to build the task, should we really emulate an async failure instead of letting the exception flow synchronously? I am thinking about this. Personally I would not put all those try catch for converting exceptions to failed task.)
One of the major concerns about async NHibernte is that Session is not thread-safe.... |
That is not a problem when using the |
The session itself does not rely on thread context, but the session factory may through the The user should be aware that using such session contextes with async await is an error, so I do not think it would be an issue. But we need to provide an |
Checking the code, there are some other usages of
|
Other subject: will |
About session context classes, I agree with you that we should provide an Currently the async version of |
Fixed the generation of the |
By looking at the log that the generator produces, there are few members that we could refactor in order to make them async: |
|
int rowsAffected; | ||
try | ||
{ | ||
rowsAffected = currentBatch.ExecuteNonQuery(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is strange...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is fine. CurrentBatch does not have async version of ExecuteNonQuery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit unfortunate though, that looks to me as the main IO of flushes. But current concrete batcher implementations, be it System.Data.SqlClient.SqlCommandSet
or MySql.Data.MySqlClient.MySqlDataAdapter
do not have async methods for this. Now if someday they start having it, the generator will not be able to catch them since we currently interfacing with those types by reflection.
This is not correct. There are situations where the continuation code is not executed on the same thread, even if you don't call
But all libraries should always call |
You are right, thanks to pointing it out as I completely forgot about this. To recap about the thread safe concern,
To avoid having problem with thread safety in async code we must:
As we agree, we should use |
You can find a fairly recent discussion about this in dotnet/efcore#2108 (comment). Even if EF Core doesn't use One more point about thread safety: Just like EF6 and EF Core have it documented, NHibernate should then also warn its users to not run multiple async calls in parallel. If possible, it should also detect this and throw an exception like EF6 does. Although it seems like this detection did not make it in EF Core, as can be seen by the not very helpful exceptions that are thrown in this case according to this Stack Overflow question: http://stackoverflow.com/questions/43496608/avoid-entity-framework-error-with-multiple-tasks-running-concurrently-on-same-db |
Excuse me if my question is due to lack of knowledge about
Do you mean we should revert the change about directly returning Task wherever possible instead of awaiting them? Or do you just mean that when we have multiple tasks to await for performing a job, we must await them sequentially, instead of using multiple awaiting calls (
|
It's the latter. Here is the wording from EF Core:
The former point, directly returning a |
An example of what I meant:
|
So to cover all cases while not excluding valid ones:
|
I like the wording of EF Core more. You focus on parallel running tasks, but the second operation doesn't have to be async to cause threading problems. |
It took me some time but finally the generator is now able to generate the async code for the test project. @hazzik please re-generate :) Beside that there are some things left on the NHibernate side (moved to first message). |
Alexander will likely not regenerate before June. And I have not yet looked at what should be done for doing it myself. |
@maca88 I did regenerate the project (did not push it yet). For some reason the generator inserts "CancellationToken.None" to every method call in tests, even if the method has a default argument. For example:
None of these |
Re: my last comment. There are more then a 10,000 calls with |
Strange, with the above fix now we have 117 usages of |
Good enough for me:) |
Not really, here is an example, where they are still not required:
It seems that the glitch in a EDIT: All "extra" CancellationToken.None are generated for ADO.NET code. |
|
By the way @maca88, small typo it seems in log message: "use SuppressDiagnosticFaliures option". |
@hazzik, that seems to be the new error detection added by maca88. There seems to be some misunderstanding here. @maca88, are you saying the new vbproj format cannot currently be supported by AsyncGenerator due to current tooling conditions? Should we exclude it somehow from AsyncGenerator processing? |
@fredericDelaporte will fix the typo, thanks
As the OpenSolutionAsync does not have any overload to exclude some project from loading the only option is to load each project separately which the generator currently does not support it, so for now, the |
The visual studio update to 15.3.2 caused the problem. I took the workaround from here until a proper solution will be available. Edit: Now the generator supports loading each project separately so the
|
Thanks @maca88, good job. A recent change will cause now a release build failure: warnings are no more accepted. But AsyncGenerator does generate in |
It seems that the async generator needs to do two passes for taking into account new features (like the Linq DML in this case). The first pass add async to the feature, the second pass add it to the tests. |
@fredericDelaporte, now the generator scans also fields usages and will not generate an unused field anymore.
There was a bug when configuring projects separately.
The code generation when having multiple projects is always sequential (before generating code for the second project the first must be completed), otherwise the end result may be different each time. The |
Great, now all builds should succeed. |
I have done Alexander's suggested change on |
@@ -2442,7 +2442,7 @@ public static IFutureValue<TSource> ToFutureValue<TSource>(this IQueryable<TSour | |||
{ | |||
var provider = GetNhProvider(source); | |||
var future = provider.ExecuteFuture<TSource>(source.Expression); | |||
return new FutureValue<TSource>(() => future, async cancellationToken => await future.GetEnumerableAsync(cancellationToken).ConfigureAwait(false)); | |||
return new FutureValue<TSource>(future.GetEnumerable, async cancellationToken => await future.GetEnumerableAsync(cancellationToken).ConfigureAwait(false)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for not simply writing
return new FutureValue<TSource>(future.GetEnumerable, future.GetEnumerableAsync);
instead for this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, I think before the return types were different (IList<T>
-> IEnumerable<T>
) so this was needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is there an IList<T>
? Both are Task<IEnumerable<T>>
. (The method argument return type and GetEnumerableAsync
.) So if there is no other reasons, I simplify it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, IAsyncEnumerable<>
was the reason why as we had an anonymous funtion where we called the ToList
method to get an IEnumerable<>
. Now this is not needed anymore so the simplification is the way to go.
Some things left on the NHibernate side:
<include />
tag (example), why not use<inheritdoc />
tag instead?ConfigureAwait(false)
for the manually written code[ThreadStatic]
withAsyncLocal<>
on theSessionIdLoggingContext
CancellationToken
parameters to manually written async methods (linq extensions and future)