In the following code:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
private async Task<bool> Refresh()
{
log.Info("Refreshing Token.");
Debug.WriteLine("Refreshing Token.");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TOKEN_URL);
request.Method = "POST";
request.ContentType = "application/json; charset=UTF-8";
request.Headers.Add("Authorization", "basic " + authCode);
request.Accept = "application/json, text/javascript, */*; q=0.01";
request.ContentLength = payload.Length;
log.Debug(request.Headers["Authorization"]);
Debug.WriteLine(request.Headers["Authorization"]);
using (Stream writeStream = request.GetRequestStream())
{
await writeStream.WriteAsync(payload, 0, payload.Length);
}
lock (tokenLock)
{
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
tokenLock.EnterWriteLock();
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
}
try
{
string body;
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
int numericStatusCode = (int)response.StatusCode;
Debug.WriteLine($"Response Code: {numericStatusCode}");
if (response.StatusCode != HttpStatusCode.OK)
{
log.Error($"!!!!! Request failed. Received HTTP {response.StatusCode}");
body = string.Empty;
}
else
{
string responseValue = string.Empty;
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
{
using (StreamReader reader = new StreamReader(responseStream))
{
responseValue = await reader.ReadToEndAsync();
}
}
}
body = responseValue;
Debug.WriteLine($"Response Body = {body}");
log.Trace($"Response Body = {body}");
}
}
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
if (!string.IsNullOrEmpty(body))
{
_token = JsonConvert.DeserializeObject<AuthTokenInfo>(body, serializerSettings);
refreshUri = _token.RefreshTokenServerUri;
payload = Encoding.GetEncoding("utf-8").GetBytes(
JsonConvert.SerializeObject(new { grant_type = "refresh_token", _token.RefreshToken })
);
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
Debug.WriteLine($"Token Refreshed, Expires In = {_token.ExpiresIn}");
Debug.WriteLine($"Access Token = {_token.AccessToken}");
Debug.WriteLine($"New Token Refresh URI: {refreshUri}");
Debug.WriteLine($"New Refresh Token: {_token.RefreshToken}");
if (_token != null)
{
int refreshTime = 60 * 1000; // (Token.ExpiresIn.Value - (15 * 60)) * 1000;
log.Info($"Refreshing token in {refreshTime} milliseconds.");
Debug.WriteLine($"Refreshing token in {refreshTime} milliseconds.");
Task.Delay(refreshTime).ContinueWith(async (action) =>
{
log.Info("Refreshing token NOW.");
Debug.WriteLine("Refreshing token NOW.");
await Refresh();
});
Debug.WriteLine("Refresh scheduled.");
}
}
}
finally
{
lock(tokenLock)
{
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
tokenLock.ExitWriteLock();
Debug.WriteLine($"Write Lock enabled? {tokenLock.IsWriteLockHeld}");
}
}
return true;
}
When I execute this code, my debug output shows:
Refreshing Token.
Write Lock enabled? False
Write Lock enabled? True
Response Code: 200
Write Lock enabled? False
Write Lock enabled? False
Token Refreshed, Expires In = 3600
Refreshing token in 60000 milliseconds.
Refresh scheduled.
Write Lock enabled? False
Exception thrown: 'System.Threading.SynchronizationLockException' in System.Core.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
Exception thrown: 'System.TypeInitializationException' in InContactApi.dll
Exception thrown: 'System.TypeInitializationException' in mscorlib.dll
Exception thrown: 'System.AggregateException' in mscorlib.dll
An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
One or more errors occurred.
Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.TypeInitializationException: The type initializer for 'InContact.Auth' threw an exception. ---> System.AggregateException: One or more errors occurred. ---> System.Threading.SynchronizationLockException: The write lock is being released without being held.
at System.Threading.ReaderWriterLockSlim.ExitWriteLock()
at InContact.AuthToken.<Refresh>d__12.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 206
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at InContact.AuthToken..ctor() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 106
at InContact.Auth..cctor() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\Auth.cs:line 236
--- End of inner exception stack trace ---
at InContact.Auth.get_BaseURL()
at InContact.InContactApi.MakeRequestURL(String subURL, Dictionary`2 query, String callerName) in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\InContactApi.cs:line 127
at InContact.InContactApi.<GetFolderListing>d__26.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\InContactApi\InContactApi\InContactApi.cs:line 607
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at CallLogger.ICRecordings.<DirTraverse>d__8.MoveNext() in C:\Users\chill\source\repos\interactive_intelligence\CallLogger\CallLogger\ICRecordings.cs:line 73
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
at System.Threading.Tasks.Task`1.get_Result()
at CallLogger.Program.Main() in C:\Users\chill\source\repos\interactive_intelligence\CallLogger\CallLogger\Program.cs:line 32
The program '[34036] PhoneLogger.exe' has exited with code 0 (0x0).
I am not understanding how my write lock is unlocking partway through the code, with the only write unlock line I have being at the end in a finally block.
Can anyone shed some light on this for me, and/or suggest a better approach?
I am dealing with an OAuth system that has an access token that must be refreshed every hour (when I am finally done with it). I have implemented this Refresh method to accomplish the goal, and use a Task.Delay().ContinueWith()
call to schedule the refresh to run automatically. I am using a ReadWriterLockSlim so that I can lock the reads from continuing while the refresh is happening. Otherwise I want them to work normally. I need them locked because once I request the new token from the server on the refresh, I can no longer use the old auth token.