Skip to content
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

Task-spawning code refactoring #4475

Open
MaceWindu opened this issue Apr 11, 2024 · 2 comments
Open

Task-spawning code refactoring #4475

MaceWindu opened this issue Apr 11, 2024 · 2 comments

Comments

@MaceWindu
Copy link
Contributor

We should review/fix/simplify places where we create and run Tasks, e.g.

https://github.com/linq2db/linq2db/blob/master/Source/LinqToDB/AsyncExtensions.cs#L30-L68

this leads to exceptions like:

Message: 'Start may not be called on a task that has completed.',
    Stack: '   at System.Threading.Tasks.Task.Start(TaskScheduler scheduler)
   at LinqToDB.AsyncExtensions.GetTask[T](Func`1 func, CancellationToken token)
   at LinqToDB.AsyncExtensions.ToArrayAsync[TSource](IQueryable`1 source, CancellationToken token)

https://github.com/linq2db/linq2db/blob/master/Source/LinqToDB/LinqExtensions.cs#L732-L735

this is not necessary, as we should always work with IQueryProviderAsync

@jods4
Copy link
Contributor

jods4 commented Apr 12, 2024

Regarding AsyncExtensions functions that create tasks, I agree they could all be removed in favor of Task.Run() overloads, which should be avail. since .net 4.5?

BTW I believe your exception arises when calling Start() on a task that was created with a cancellation token that is already cancelled when you call Start().
Because of this, using new Task(cancellationToken) when the token is not under you control and properly synchronized, is a race condition hazard and should be avoided.

RE the LinqExtensions piece of code... I have so many questions 😆

  • I am not sure whether the provider can possibly not be IQueryProviderAsync.
  • Doing sync over async is just worst than sync, I'd even prefer if the code executed sync and returned a completed Task...
  • I don't know the history of linq2db option ContinueOnCapturedContext and it's a weird option.

The pattern for that last point is unlike general .net guidance.
Generally libraries such as linq2db should call .ConfigureAwait(false) in their async functions so that internal work is not bound to a specific scheduler (UI thread or http request thread). Application code is then free to do whatever they want at their level (which is typically nothing and awaited tasks are executed on their main thread scheduler).

I'm wondering what use is that option for, and whether it even has the intended effect?
In the code you linked, that ConfigureAwait code has almost zero effect. Note that the task created by Task.Run is not actually returned by DeleteAsync but awaited. DeleteAsync itself is an async function, so it'll return another Task instance and the scheduler used by continuations of DeleteAsync depend on the await configuration at caller site rather than Common.Configuration.ContinueOnCapturedContext at that location... so it seems rather useless to me.

@MaceWindu
Copy link
Contributor Author

MaceWindu commented Apr 13, 2024

I believe your exception arises when calling Start() on a task that was created with a cancellation token that is already cancelled when you call Start().

yes, and while it is completely valid scenario, it adds one too much useless exception to ignore when we already have Task/Operation cancelled duo...

... ContinueOnCapturedContext ...

it was added in times when we had little idea about this stuff to just please everyone :). Probably it's a good idea to get rid of it in v6 finally

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants