Invisible race conditionsThe async query
This issue was reported to the mailing list with a really scary error: UseAfterFree detected! Attempt to return memory from previous generation, Reset has already been called and the memory reused!
I initially read it as an error that is raised from the server, which raised up all sort of flags and caused us to immediately try to track down what is going on.
Here is the code that would reproduce this:
And a key part of that is that this is not happening on the server, but on the client. You now have all the information required to see what the error is.
Can you figure it out?
The problem is that this method returns a Task, but it isn’t an async method. In other words, we return a task that is still running from ToListAsync, but because we aren’t awaiting on it, the session’s dispose is going to run, and by the time the server request completes and is ready to actually do something with the data that it go, we are already disposed and we get this error.
The solution? Turn this into an async method and await on the ToListAsync() before disposing the session.
More posts in "Invisible race conditions" series:
- (02 Jan 2018) The cache has poisoned us
- (27 Dec 2017) The sometimes failing script
- (26 Dec 2017) The async query
Comments
I wonder if Microsoft is barking up the wrong tree with async. Java is considering support for fibers, in other words, transparent async. At the interop level async calls may be used but higher up the call chain the continuation is done automatically. The managed code is from the runtime perspective paused and later on continued on possibly another thread once the async call completes. In .NET we currently have allocation heavy state machines all the way down and a very leaky abstractions of which one must know all the under-the-cover details.
Sebastiaan, That would make for a nicer model, yes. But .NET already looked at fibers very heavily during the 2005 time frame. The end result was that it wasn't feasible. There are a lot of data structures that either have thread affinity or use thread local values. More to the point, there is a lot of code that make such assumptions, you don't want to have this happen automatically for you without realizing it.
Sebastiaan, that's a good point.
Most "line of business app" code gains zero value from async. Zero additional throughput. Almost all C# should not use async. We have not used async for 15 years in .NET and it worked just fine. Our services performed and scaled just fine on threads.
Certain kinds of code such as RavenDB surely benefit immensely. Also, client GUI development becomes more convenient. The typical ASP.NET app does not need async but is harmed by it. And in a sense that's a solution to the problem that you correctly pointed out.
tobi, a lot of that depends on what you mean by "value". If you can handle 10 times more users, or have more complex processes more easily, that is certainly value.
In 99% of IO cases this value does not occur in LOB apps. Can just run with more threads. I routinely see 100 threads in production apps, it's nothing. Backend (DB, 3rd party service) usually is the bottleneck. Async would not help with that in any way.
This is what I observe in customer apps, my apps and on Stack Overflow.
I also regularly find cases for surgical insertion of an async IO path. No need to pollute the entire code base. Make one MVC action or whatever it is async if needed. Candidates are high-volume high-latency operations. Most ops are not that.
Comment preview