Session execute Ensure on only single Task.
This commit is contained in:
@@ -27,6 +27,8 @@ public sealed class Session
|
||||
private TimeSpan _nextLoginBackoff;
|
||||
private DateTime _nextValidTrial;
|
||||
|
||||
private Task<bool>? _ensureValidTask;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a session from credentials and optionally a possibly live token.
|
||||
/// </summary>
|
||||
@@ -80,11 +82,21 @@ public sealed class Session
|
||||
// Fast track positive check
|
||||
return true;
|
||||
|
||||
if (await RefreshUserDataAsync(cancel) && (_isVip ?? false))
|
||||
// Serialize threads so we do not bounce the server too much
|
||||
TaskCompletionSource<bool> task = new TaskCompletionSource<bool>();
|
||||
Task<bool>? resultT = Interlocked.CompareExchange(ref _ensureValidTask, task.Task, null);
|
||||
if (resultT != null)
|
||||
// We are not the thread that will determine it
|
||||
return await resultT;
|
||||
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
if (_token != null && await RefreshUserDataAsync(_token, cancel) && (_isVip ?? false))
|
||||
{
|
||||
// Valid VIP (slower with API check)
|
||||
_nextValidTrial = now + TokenValidPeriod;
|
||||
return true;
|
||||
return result = true;
|
||||
}
|
||||
|
||||
if (now > _nextLoginTrial)
|
||||
@@ -106,13 +118,13 @@ public sealed class Session
|
||||
token = null;
|
||||
}
|
||||
|
||||
if (token != null && await RefreshUserDataAsync(cancel) && (_isVip ?? false))
|
||||
if (token != null && await RefreshUserDataAsync(token, cancel) && (_isVip ?? false))
|
||||
{
|
||||
// Valid token with VIP
|
||||
_nextLoginBackoff = InitialLoginBackoff;
|
||||
_token = token;
|
||||
_nextValidTrial = now + TokenValidPeriod;
|
||||
return true;
|
||||
return result = true;
|
||||
|
||||
}
|
||||
else
|
||||
@@ -129,7 +141,13 @@ public sealed class Session
|
||||
_token = null;
|
||||
_isVip = null;
|
||||
_vipDaysLeft = null;
|
||||
return false;
|
||||
return result = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
task.SetResult(result);
|
||||
GC.KeepAlive(Interlocked.Exchange(ref _ensureValidTask, null));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -226,7 +244,7 @@ public sealed class Session
|
||||
/// Gets user data and indirectly validates the session token.
|
||||
/// </summary>
|
||||
/// <returns>True if refresh has been successful and session token is valid. -or- False otherwise.</returns>
|
||||
private async Task<bool> RefreshUserDataAsync(CancellationToken cancel)
|
||||
private async Task<bool> RefreshUserDataAsync(string? token, CancellationToken cancel)
|
||||
{
|
||||
_isVip = null;
|
||||
_vipDaysLeft = null;
|
||||
@@ -234,7 +252,7 @@ public sealed class Session
|
||||
// Example response: <?xml version="1.0" encoding="UTF-8"?><response><status>OK</status>...??...<app_version>30</app_version></response>
|
||||
Uri dataUri = new Uri(WebshareApiUri, "user_data/");
|
||||
HttpResponseMessage dataRes = await Program._http.PostAsync(dataUri, new FormUrlEncodedContent(new[] {
|
||||
new KeyValuePair<string, string>(TokenKeyName, _token ?? "")
|
||||
new KeyValuePair<string, string>(TokenKeyName, token ?? "")
|
||||
}), cancel
|
||||
);
|
||||
if (!dataRes.IsSuccessStatusCode)
|
||||
|
||||
Reference in New Issue
Block a user