mirror of
https://github.com/bitwarden/mobile
synced 2026-01-04 17:43:17 +00:00
[PM-1576] Fix Race condition AccountsManager registration (#2434)
* PM-1576 Moved registration of AccountsManager to avoid race conditions with the app start. To do so, added ConditionedAwaiterManager so that it handles a task to be awaited or completed depending on the callers. * PM-1576 Fix format * PM-1576 Fix throw to preserve StackTrace
This commit is contained in:
committed by
GitHub
parent
e5ce1760a6
commit
1823efa0e5
17
src/Core/Abstractions/IConditionedAwaiterManager.cs
Normal file
17
src/Core/Abstractions/IConditionedAwaiterManager.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public enum AwaiterPrecondition
|
||||
{
|
||||
EnvironmentUrlsInited
|
||||
}
|
||||
|
||||
public interface IConditionedAwaiterManager
|
||||
{
|
||||
Task GetAwaiterForPrecondition(AwaiterPrecondition awaiterPrecondition);
|
||||
void SetAsCompleted(AwaiterPrecondition awaiterPrecondition);
|
||||
void SetException(AwaiterPrecondition awaiterPrecondition, Exception ex);
|
||||
}
|
||||
}
|
||||
42
src/Core/Services/ConditionedAwaiterManager.cs
Normal file
42
src/Core/Services/ConditionedAwaiterManager.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class ConditionedAwaiterManager : IConditionedAwaiterManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<AwaiterPrecondition, TaskCompletionSource<bool>> _preconditionsTasks = new ConcurrentDictionary<AwaiterPrecondition, TaskCompletionSource<bool>>
|
||||
{
|
||||
[AwaiterPrecondition.EnvironmentUrlsInited] = new TaskCompletionSource<bool>()
|
||||
};
|
||||
|
||||
public Task GetAwaiterForPrecondition(AwaiterPrecondition awaiterPrecondition)
|
||||
{
|
||||
if (_preconditionsTasks.TryGetValue(awaiterPrecondition, out var tcs))
|
||||
{
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void SetAsCompleted(AwaiterPrecondition awaiterPrecondition)
|
||||
{
|
||||
if (_preconditionsTasks.TryGetValue(awaiterPrecondition, out var tcs))
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetException(AwaiterPrecondition awaiterPrecondition, Exception ex)
|
||||
{
|
||||
if (_preconditionsTasks.TryGetValue(awaiterPrecondition, out var tcs))
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -13,13 +14,16 @@ namespace Bit.Core.Services
|
||||
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IConditionedAwaiterManager _conditionedAwaiterManager;
|
||||
|
||||
public EnvironmentService(
|
||||
IApiService apiService,
|
||||
IStateService stateService)
|
||||
IStateService stateService,
|
||||
IConditionedAwaiterManager conditionedAwaiterManager)
|
||||
{
|
||||
_apiService = apiService;
|
||||
_stateService = stateService;
|
||||
_conditionedAwaiterManager = conditionedAwaiterManager;
|
||||
}
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
@@ -52,30 +56,44 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task SetUrlsFromStorageAsync()
|
||||
{
|
||||
var urls = await _stateService.GetEnvironmentUrlsAsync();
|
||||
if (urls == null)
|
||||
try
|
||||
{
|
||||
urls = await _stateService.GetPreAuthEnvironmentUrlsAsync();
|
||||
}
|
||||
if (urls == null)
|
||||
{
|
||||
urls = new EnvironmentUrlData();
|
||||
}
|
||||
var envUrls = new EnvironmentUrls();
|
||||
if (!string.IsNullOrWhiteSpace(urls.Base))
|
||||
{
|
||||
BaseUrl = envUrls.Base = urls.Base;
|
||||
var urls = await _stateService.GetEnvironmentUrlsAsync();
|
||||
if (urls == null)
|
||||
{
|
||||
urls = await _stateService.GetPreAuthEnvironmentUrlsAsync();
|
||||
}
|
||||
if (urls == null)
|
||||
{
|
||||
urls = new EnvironmentUrlData();
|
||||
}
|
||||
var envUrls = new EnvironmentUrls();
|
||||
if (!string.IsNullOrWhiteSpace(urls.Base))
|
||||
{
|
||||
BaseUrl = envUrls.Base = urls.Base;
|
||||
_apiService.SetUrls(envUrls);
|
||||
|
||||
_conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.EnvironmentUrlsInited);
|
||||
return;
|
||||
}
|
||||
|
||||
BaseUrl = urls.Base;
|
||||
WebVaultUrl = urls.WebVault;
|
||||
ApiUrl = envUrls.Api = urls.Api;
|
||||
IdentityUrl = envUrls.Identity = urls.Identity;
|
||||
IconsUrl = urls.Icons;
|
||||
NotificationsUrl = urls.Notifications;
|
||||
EventsUrl = envUrls.Events = urls.Events;
|
||||
_apiService.SetUrls(envUrls);
|
||||
return;
|
||||
|
||||
_conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.EnvironmentUrlsInited);
|
||||
}
|
||||
BaseUrl = urls.Base;
|
||||
WebVaultUrl = urls.WebVault;
|
||||
ApiUrl = envUrls.Api = urls.Api;
|
||||
IdentityUrl = envUrls.Identity = urls.Identity;
|
||||
IconsUrl = urls.Icons;
|
||||
NotificationsUrl = urls.Notifications;
|
||||
EventsUrl = envUrls.Events = urls.Events;
|
||||
_apiService.SetUrls(envUrls);
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
_conditionedAwaiterManager.SetException(AwaiterPrecondition.EnvironmentUrlsInited, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<EnvironmentUrlData> SetUrlsAsync(EnvironmentUrlData urls)
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Bit.Core.Utilities
|
||||
|
||||
SearchService searchService = null;
|
||||
|
||||
var conditionedRunner = new ConditionedAwaiterManager();
|
||||
var tokenService = new TokenService(stateService);
|
||||
var apiService = new ApiService(tokenService, platformUtilsService, (extras) =>
|
||||
{
|
||||
@@ -82,12 +83,13 @@ namespace Bit.Core.Utilities
|
||||
keyConnectorService, passwordGenerationService);
|
||||
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
||||
var auditService = new AuditService(cryptoFunctionService, apiService);
|
||||
var environmentService = new EnvironmentService(apiService, stateService);
|
||||
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
||||
var eventService = new EventService(apiService, stateService, organizationService, cipherService);
|
||||
var userVerificationService = new UserVerificationService(apiService, platformUtilsService, i18nService,
|
||||
cryptoService);
|
||||
var usernameGenerationService = new UsernameGenerationService(cryptoService, apiService, stateService);
|
||||
|
||||
Register<IConditionedAwaiterManager>(conditionedRunner);
|
||||
Register<ITokenService>("tokenService", tokenService);
|
||||
Register<IApiService>("apiService", apiService);
|
||||
Register<IAppIdService>("appIdService", appIdService);
|
||||
|
||||
Reference in New Issue
Block a user