Session execute Ensure on only single Task.

This commit is contained in:
2024-12-07 02:28:54 +01:00
parent 882cf0f604
commit 1bf5fc675e

View File

@@ -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)