I have a program that runs on a server communicating with various web sites. One of the web sites will soon require the communication to be via http POST requests, so a new version of the program is required. Occasionally the program will need to issue a few hundred POST requests simultaneously, so making the requests asynchronously will be important for performance. I have now tried two ways of coding this, and both ways fail intermittently in exactly the same way by throwing an AccessViolationException. This happens even though the program consists entirely of managed code, so that AccessViolationExceptions should be impossible (see Microsoft's remarks on the AccessViolationException Class).
I posted a question about my first method of coding this two weeks ago (see System.AccessViolationException at ...). Since then I discovered the System.Net.Http.HttpClient
class, and my second method of coding tries to use that, and hence it's completely different from the first method. The heart of the revised code looks like this:
HttpClient httpclient = new HttpClient();
// set up httpclient with headers etc
System.Threading.CancellationTokenSource cts = new System.Threading.CancellationTokenSource();
using (StringContent sc = new StringContent(stringContent)) {
sc.Headers.ContentType = new MediaTypeWithQualityHeaderValue(ContentType);
using (Task<HttpResponseMessage> task_postasync = httpclient.PostAsync(EndPoint, sc, cts.Token)) {
task_postasync.Wait();
if (!task_postasync.IsCompleted) {
// cater for failure
}
using (HttpResponseMessage response = task_postasync.Result) {
using (Task<string> task_ReadAsStringAsync = response.Content.ReadAsStringAsync()) {
task_ReadAsStringAsync.Wait();
if (!task_ReadAsStringAsync.IsCompleted) {
// cater for failure
}
string str = task_ReadAsStringAsync.Result;
// str is the now the result of the http POST request :-)
}
}
}
}
Both the revised and the original code work most of the time, but occasionally they both fail by throwing an AccessViolationException
. Furthermore, with both code versions the location of the exception is specified as System.Threading._IOCompletionCallback.PerformIOCompletionCallback()
. This suggests to me that both versions fail as a result of a hitting the same low level bug in the .Net libraries. The problem arises whether I run the code as 32 bit or 64 bit.
On the server the minimum number of completion ports in the ThreadPool defaults to 24 and the maximum number defaults to 1000. Most of the time I have been setting the minimum number to the maximum of 1000, however I still get the AccessViolationException
when I leave everything at the default values.
I tried looking at where the exception gets thrown in assembler, by running with unmanaged code debugging enabled. The exception is thrown when the instruction pointer EIP
is 7606336A
in the following code:
76063354 nop
76063355 nop
76063356 nop
76063357 nop
76063358 mov edi,edi
7606335A push ebp
7606335B mov ebp,esp
7606335D test ecx,ecx
7606335F jne 760661DA
76063365 push dword ptr [ebp+8]
76063368 call edx
7606336A push eax
7606336B call dword ptr ds:[76060704h]
76063371 nop
76063372 nop
76063373 nop
76063374 nop
76063375 nop
76063376 mov edi,edi
76063378 push ebp
76063379 mov ebp,esp
7606337B sub esp,62Ch
76063381 mov eax,dword ptr ds:[761303ACh]
76063386 xor eax,ebp
76063388 mov dword ptr [ebp-4],eax
7606338B cmp dword ptr [ebp+8],1
7606338F push esi
76063390 mov esi,dword ptr [ebp+0Ch]
76063393 je 76066AF1
76063399 cmp dword ptr [ebp+8],2
7606339D jne 760633A8
7606339F cmp byte ptr ds:[76130002h],0
The exception is thrown immediately after the line call edx
and the register edx
is zero, so this looks to me as though it's trying to call something with memory address zero and failing.
To help optimise large number of simultaneous web requests the program has a config file which is as follows:
<?xml version="1.0"?>
<configuration>
<system.net>
<connectionManagement>
<add address="*" maxconnection="5000"/>
</connectionManagement>
<settings>
<servicePointManager expect100Continue="false" useNagleAlgorithm="false"/>
</settings>
</system.net>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
These have been the settings for a long time, and have been useful in the past for getting high performance with multiple requests over the internet.
I would have thought that making asynchronous http POST requests should be easy, but I don't seem to be able to avoid an intermittent AccessViolationException
. Can anyone help?