With the .Net framework 4, I'm getting a System.AccessViolationException in multi-threaded C# code, even though all my code is managed code. Could this be a bug in .Net, because I always thought that using .Net meant that whatever programming errors I make, AccessViolationException shouldn't happen?
The only error message that I get from Visual Studio 2010 is
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
There's no call stack information (it just says [External Code]
), and that's true even when I turn on unmanaged debugging to try and see the native call stack.
My application is making multiple simultaneous http calls over the public internet to an API which returns JSON objects. All calls are via a class AsyncResult which I've pasted below. Any suggestions on how to solve this would be most welcome.
public class AsyncResult<T> : IDisposable where T : class {
private const int NumPasses = 2; // if Exception thrown on first pass, try again in case it was an internet problem
public readonly System.Threading.ManualResetEvent allDone;
public readonly string method;
public readonly IDictionary<string, object> args;
private readonly RpcClient.CreateWebRequestDelegate cwr_deleg;
private readonly VoidVoidDelegate cb; // to be called when Async operation complete or abandoned
public readonly string LocationDescripForError;
private int m_count; // number of passes. Call should execute on first pass
private bool m_Abort; // mechanism for aborting from outside the object
private int m_Stage; // 0 = waiting for GetRequestStreamCallback, 1 = waiting for GetResponseCallback, 2 or greater = waiting for ReadStreamCallBack
private System.Exception m_exception; // non null if an exception has occurred
private WebRequest request;
private IAsyncResult iasr_GetRequestStream;
private IAsyncResult iasr_GetResponse;
private IAsyncResult iasr_streamRead;
private WebResponse response;
private Stream responseStream;
private const int BUFFER_SIZE = 1024;
private byte[] buffer;
private StringBuilder sb_jsonResponse;
private T result;
public AsyncResult(string method, IDictionary<string, object> args, RpcClient.CreateWebRequestDelegate cwr_deleg, string LocationDescripForError, VoidVoidDelegate cb) {
allDone = new System.Threading.ManualResetEvent(false);
this.method = method; this.args = args; this.cwr_deleg = cwr_deleg; this.LocationDescripForError = LocationDescripForError; this.cb = cb;
m_count = 0; m_Abort = false; buffer = new byte[BUFFER_SIZE]; sb_jsonResponse = new StringBuilder("");
InitiateCallSequence();
}
~AsyncResult() { try { Dispose(); } catch {} }
private void InitiateCallSequence() {
if (m_Abort) return;
m_count++;
m_Stage = 0; m_exception = null; sb_jsonResponse.Clear();
request = cwr_deleg();
iasr_GetRequestStream = (IAsyncResult)request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), null);
}
private void ExceptionHandling(System.Exception Ex) {
if (m_count < NumPasses) {
try {
iasr_GetRequestStream = null; InitiateCallSequence();
if (iasr_GetRequestStream != null) return;
} catch { }
}
m_exception = Ex;
lock (allDone) {
if (!m_Abort) allDone.Set();
}
if (cb != null) cb();
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult) {
System.Threading.Interlocked.Increment(ref m_Stage);
try {
using (Stream streamResponse = request.EndGetRequestStream(asynchronousResult)) {
if (m_Abort) return;
using (StreamWriter writer = new StreamWriter(streamResponse, Encoding.UTF8)) {
Request call = new Request { Method = method, Id = 1, Params = args };
Convert.Export(call, writer);
}
}
iasr_GetResponse = (IAsyncResult)request.BeginGetResponse(new AsyncCallback(GetResponseCallback), null);
} catch (System.Exception Ex) { ExceptionHandling(Ex); }
}
private void GetResponseCallback(IAsyncResult asynchronousResult) {
System.Threading.Interlocked.Increment(ref m_Stage);
try {
response = request.EndGetResponse(asynchronousResult); // End the Asynchronous response.
if (m_Abort) return;
// Read the response into a 'Stream' object.
responseStream = response.GetResponseStream();
iasr_streamRead = responseStream.BeginRead(buffer, 0, BUFFER_SIZE, new AsyncCallback(ReadStreamCallBack), null);
} catch (System.Exception Ex) { ExceptionHandling(Ex); }
}
private void ReadStreamCallBack(IAsyncResult asynchronousResult) {
System.Threading.Interlocked.Increment(ref m_Stage);
try {
int read = responseStream.EndRead(asynchronousResult); // End the Asynchronous read.
if (m_Abort) return;
if (read > 0) {
sb_jsonResponse.Append(Encoding.ASCII.GetString(buffer, 0, read));
iasr_streamRead = responseStream.BeginRead(buffer, 0, AsyncResult<T>.BUFFER_SIZE, new AsyncCallback(ReadStreamCallBack), null);
} else {
Response<T> jsonResponse = Convert.Import<T>(sb_jsonResponse.ToString());
if (jsonResponse.HasError) {
System.Exception Ex = ReconstituteException(jsonResponse.Error, LocationDescripForError);
ExceptionHandling(Ex);
return;
} else {
result = jsonResponse.Result;
}
responseStream.Dispose();
responseStream.Close();
if (m_count > 1) System.Diagnostics.Trace.WriteLine("AsyncResult success count=" + m_count);
lock (allDone) {
if (!m_Abort) allDone.Set();
}
if (cb != null) cb();
}
} catch (System.Exception Ex) { ExceptionHandling(Ex); }
}
public T GetResult(int millisecondsTimeout) {
bool waitresult = allDone.WaitOne(millisecondsTimeout);
if (waitresult)
return (m_exception == null ? result : null);
else {
Abort();
return null;
}
}
public bool IsError { get { return (m_exception != null); } }
public string ErrorMessage { get {
if (m_exception == null) return string.Empty;
WebException wex = (m_exception is WebException ? (WebException)m_exception : null);
string retval = CoreLogging.ExceptionDescription(m_exception) + " API-NG(" + method + ":" + m_Stage + "): " + m_exception.Message + (string.IsNullOrWhiteSpace(m_exception.Source) ? "" : ", Source=" + m_exception.Source) + (wex == null ? "" : ", Status=" + wex.Status);
return retval;
} }
public int GetStage() { return m_Stage; }
public void Abort() { m_Abort = true; Dispose(); }
public bool IsAborted { get { return m_Abort; } }
public void Dispose() { lock (allDone) { allDone.Dispose(); } }
}