I'm running into a problem with TPL dataflow that I can't seem to figure out. My code will run anywhere from 15 minutes to a couple of hours before it just deadlocks. I've done as much digging as I can and I will try to post it here.
Also, I've read that you generally shouldn't mix asynchronous and blocking code. If I'm doing it wrong by isolating my TPL Dataflow code to a Task.Run(Func<Task<bool>>) call, let me know. Otherwise, I don't think this is the problem since it's running on its own thread.
My dataflow network is set up pretty much exactly as in this SO link, please refer to it for the code.
The TransformBlock is pretty involved with lots of various awaits. One of the functions calls await httpRequest.GetRequestStreamAsync(). This seems to be where the deadlock is occurring, because running a memory profiler reveals four compiler-generated IAsyncStateMachine structs stuck in memory (e.g. <ExampleAsync>d__0) that all represent a state immediately before it's called.
It also shows that 110 objects are stuck waiting to be processed in the TransformManyBlock (and by the next BatchedJoinBlock), and there are only 267 HTTP connections open (ServicePointManager.DefaultConnectionLimit is set to 512).
Checking the source code of the GetRequestStreamAsync function reveals that at least another thread is created with Task.Run<>(), which seems unnecessary to me. I'm thinking I could even just call the FromAsync function myself:
if (...)
{
return Task.Run<Stream>(() => {
TaskFactory<Stream> factory = Task<Stream>.Factory;
WebRequest webRequest = this;
WebRequest webRequest1 = this;
return factory.FromAsync(
new Func<AsyncCallback, object, IAsyncResult> (webRequest.BeginGetRequestStream),
new Func<IAsyncResult, Stream>(webRequest1.EndGetRequestStream),
null
);
});
}
Could it be that the thread pool is becoming exhausted?
My only other clue is that the BatchedJoinBlocks may be hogging resources, because even after each one is completed, they never get disposed -- which leads me to want to continue the BatchedJoinBlock.Completion task with one that will unlink itself upon completion. (Setting MaxNumberOfGroups to 1 didn't seem to do that.) I believe there were nearly 4000 lingering BatchedJoinBlocks.
I'm stumped. If something is timing out, an exception is caught and passed to the Exception target of the BatchedJoinBlock. Obviously, this isn't the case.