using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using NotNull = JetBrains.Annotations.NotNullAttribute; namespace Unity.Services.Core.Internal { /// /// Utility to initialize all Unity services from a single endpoint. /// class UnityServicesInternal : IUnityServices { /// /// Initialization state. /// public ServicesInitializationState State { get; private set; } public InitializationOptions Options { get; internal set; } internal bool CanInitialize; TaskCompletionSource m_Initialization; [NotNull] CoreRegistry Registry { get; } [NotNull] CoreMetrics Metrics { get; } [NotNull] CoreDiagnostics Diagnostics { get; } public UnityServicesInternal([NotNull] CoreRegistry registry, [NotNull] CoreMetrics metrics, [NotNull] CoreDiagnostics diagnostics) { Registry = registry; Metrics = metrics; Diagnostics = diagnostics; } /// /// Single entry point to initialize all used services. /// /// /// The options to customize services initialization. /// /// /// Return a handle to the initialization operation. /// public async Task InitializeAsync(InitializationOptions options) { if (!HasRequestedInitialization() || HasInitializationFailed()) { Options = options; m_Initialization = new TaskCompletionSource(); } if (!CanInitialize || State != ServicesInitializationState.Uninitialized) { await m_Initialization.Task; } else { await InitializeServicesAsync(); } bool HasInitializationFailed() { return m_Initialization.Task.IsCompleted && m_Initialization.Task.Status != TaskStatus.RanToCompletion; } } bool HasRequestedInitialization() { return !(m_Initialization is null); } async Task InitializeServicesAsync() { State = ServicesInitializationState.Initializing; var initStopwatch = new Stopwatch(); initStopwatch.Start(); var dependencyTree = Registry.PackageRegistry.Tree; if (dependencyTree is null) { var reason = new NullReferenceException("Services require a valid dependency tree to be initialized."); FailServicesInitialization(reason); throw reason; } var sortedPackageTypeHashes = new List(dependencyTree.PackageTypeHashToInstance.Count); List packageInitInfos; try { SortPackages(); packageInitInfos = await InitializePackagesAsync(); } catch (Exception reason) { FailServicesInitialization(reason); throw; } SendInitializationMetrics(packageInitInfos); SucceedServicesInitialization(); void SortPackages() { var sorter = new DependencyTreeInitializeOrderSorter(dependencyTree, sortedPackageTypeHashes); sorter.SortRegisteredPackagesIntoTarget(); } async Task> InitializePackagesAsync() { var initializer = new CoreRegistryInitializer(Registry, sortedPackageTypeHashes); return await initializer.InitializeRegistryAsync(); } void FailServicesInitialization(Exception reason) { State = ServicesInitializationState.Uninitialized; initStopwatch.Stop(); m_Initialization.TrySetException(reason); if (reason is CircularDependencyException) { Diagnostics.SendCircularDependencyDiagnostics(reason); } else { Diagnostics.SendOperateServicesInitDiagnostics(reason); } } void SucceedServicesInitialization() { State = ServicesInitializationState.Initialized; Registry.PackageRegistry.Tree = null; Registry.LockComponentRegistration(); initStopwatch.Stop(); m_Initialization.TrySetResult(null); Metrics.SendAllPackagesInitSuccessMetric(); Metrics.SendAllPackagesInitTimeMetric(initStopwatch.Elapsed.TotalSeconds); } } internal void SendInitializationMetrics(List packageInitInfos) { foreach (var initInfo in packageInitInfos) { Metrics.SendInitTimeMetricForPackage(initInfo.PackageType, initInfo.InitializationTimeInSeconds); } } internal async Task EnableInitializationAsync() { CanInitialize = true; Registry.LockPackageRegistration(); if (!HasRequestedInitialization()) return; await InitializeServicesAsync(); } } }