public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { var currentSolution = s_primaryWorkspace.CurrentSolution; var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); if (primarySolutionChecksum == solutionChecksum) { // nothing changed return; } using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { var updater = new SolutionCreator(_assetService, currentSolution, cancellationToken); if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // solution has updated s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); return; } // new solution. bulk sync all asset for the solution await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); s_primaryWorkspace.ClearSolution(); s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); } }
private async Task <Solution> CreateSolution_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken) { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // solution has updated return(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false)); } // new solution. bulk sync all asset for the solution await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); return(workspace.CurrentSolution); }
/// <summary> /// SolutionService is designed to be stateless. if someone asks a solution (through solution checksum), /// it will create one and return the solution. the engine takes care of synching required data and creating a solution /// correspoing to the given checksum. /// /// but doing that from scratch all the time wil be expansive in terms of synching data, compilation being cached, file being parsed /// and etc. so even if the service itself is stateless, internally it has several caches to improve perf of various parts. /// /// first, it holds onto last solution got built. this will take care of common cases where multiple services running off same solution. /// second, it uses assets cache to hold onto data just synched (within 3 min) so that if it requires to build new solution, /// it can save some time to re-sync data which might just used by other solution. /// third, it holds onto solution from primary branch from Host. and it will try to see whether it can build new solution off the /// primary solution it is holding onto. this will make many solution level cache to be re-used. /// /// the primary solution can be updated in 2 ways. /// first, host will keep track of primary solution changes in host, and call OOP to synch to latest time to time. /// second, engine keeps track of whether a certain request is for primary solution or not, and if it is, /// it let that request to update primary solution cache to latest. /// /// these 2 are complimentary to each other. #1 makes OOP's primary solution to be ready for next call (push), #2 makes OOP's primary /// solution be not stale as much as possible. (pull) /// </summary> private async Task <Solution> CreateSolution_NoLockAsync( Checksum solutionChecksum, bool fromPrimaryBranch, int workspaceVersion, Solution baseSolution, CancellationToken cancellationToken) { try { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); // check whether solution is update to the given base solution if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // create updated solution off the baseSolution var solution = await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); if (fromPrimaryBranch) { // if the solutionChecksum is for primary branch, update primary workspace cache with the solution return(PrimaryWorkspace.UpdateSolutionIfPossible(solution, workspaceVersion)); } // otherwise, just return the solution return(solution); } // we need new solution. bulk sync all asset for the solution first. await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); // get new solution info and options var(solutionInfo, options) = await GetSolutionInfoAndOptionsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); if (fromPrimaryBranch) { // if the solutionChecksum is for primary branch, update primary workspace cache with new solution if (PrimaryWorkspace.TryAddSolutionIfPossible(solutionInfo, workspaceVersion, options, out var solution)) { return(solution); } } // otherwise, just return new solution var workspace = new TemporaryWorkspace(solutionInfo, options); return(workspace.CurrentSolution); } catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceledAndPropagate(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// SolutionService is designed to be stateless. if someone asks a solution (through solution checksum), /// it will create one and return the solution. the engine takes care of synching required data and creating a solution /// correspoing to the given checksum. /// /// but doing that from scratch all the time wil be expansive in terms of synching data, compilation being cached, file being parsed /// and etc. so even if the service itself is stateless, internally it has several caches to improve perf of various parts. /// /// first, it holds onto last solution got built. this will take care of common cases where multiple services running off same solution. /// second, it uses assets cache to hold onto data just synched (within 3 min) so that if it requires to build new solution, /// it can save some time to re-sync data which might just used by other solution. /// third, it holds onto solution from primary branch from Host. and it will try to see whether it can build new solution off the /// primary solution it is holding onto. this will make many solution level cache to be re-used. /// /// the primary solution can be updated in 2 ways. /// first, host will keep track of primary solution changes in host, and call OOP to synch to latest time to time. /// second, engine keeps track of whether a certain request is for primary solution or not, and if it is, /// it let that request to update primary solution cache to latest. /// /// these 2 are complimentary to each other. #1 makes OOP's primary solution to be ready for next call (push), #2 makes OOP's primary /// solution be not stale as much as possible. (pull) /// </summary> private async Task <Solution> CreateSolution_NoLockAsync( Checksum solutionChecksum, bool fromPrimaryBranch, int workspaceVersion, Solution baseSolution, CancellationToken cancellationToken) { var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken); // check whether solution is update to the given base solution if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false)) { // create updated solution off the baseSolution var solution = await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false); if (fromPrimaryBranch) { // if the solutionChecksum is for primary branch, update primary workspace cache with the solution return(PrimaryWorkspace.UpdateSolutionIfPossible(solution, workspaceVersion)); } // otherwise, just return the solution return(solution); } // we need new solution. bulk sync all asset for the solution first. await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); // get new solution info var solutionInfo = await GetSolutionInfoAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); if (fromPrimaryBranch) { // if the solutionChecksum is for primary branch, update primary workspace cache with new solution if (PrimaryWorkspace.TryAddSolutionIfPossible(solutionInfo, workspaceVersion, out var solution)) { return(solution); } } // otherwise, just return new solution var workspace = new TemporaryWorkspace(solutionInfo); return(workspace.CurrentSolution); }
private async Task <Solution> CreateSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { // synchronize whole solution first await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); var solutionChecksumObject = await _assetService.GetAssetAsync <SolutionStateChecksums>(solutionChecksum, cancellationToken).ConfigureAwait(false); var workspace = new AdhocWorkspace(RoslynServices.HostServices, workspaceKind: WorkspaceKind_RemoteWorkspace); var solutionInfo = await _assetService.GetAssetAsync <SerializedSolutionInfo>(solutionChecksumObject.Info, cancellationToken).ConfigureAwait(false); var projects = new List <ProjectInfo>(); foreach (var projectChecksum in solutionChecksumObject.Projects) { var projectSnapshot = await _assetService.GetAssetAsync <ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false); var projectInfo = await _assetService.GetAssetAsync <SerializedProjectInfo>(projectSnapshot.Info, cancellationToken).ConfigureAwait(false); if (!workspace.Services.IsSupported(projectInfo.Language)) { // only add project our workspace supports. // workspace doesn't allow creating project with unknown languages continue; } var documents = new List <DocumentInfo>(); foreach (var documentChecksum in projectSnapshot.Documents) { var documentSnapshot = await _assetService.GetAssetAsync <DocumentStateChecksums>(documentChecksum, cancellationToken).ConfigureAwait(false); var documentInfo = await _assetService.GetAssetAsync <SerializedDocumentInfo>(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); var textLoader = TextLoader.From( TextAndVersion.Create( await _assetService.GetAssetAsync <SourceText>(documentSnapshot.Text, cancellationToken).ConfigureAwait(false), VersionStamp.Create(), documentInfo.FilePath)); // TODO: do we need version? documents.Add( DocumentInfo.Create( documentInfo.Id, documentInfo.Name, documentInfo.Folders, documentInfo.SourceCodeKind, textLoader, documentInfo.FilePath, documentInfo.IsGenerated)); } var p2p = new List <ProjectReference>(); foreach (var checksum in projectSnapshot.ProjectReferences) { cancellationToken.ThrowIfCancellationRequested(); var reference = await _assetService.GetAssetAsync <ProjectReference>(checksum, cancellationToken).ConfigureAwait(false); p2p.Add(reference); } var metadata = new List <MetadataReference>(); foreach (var checksum in projectSnapshot.MetadataReferences) { cancellationToken.ThrowIfCancellationRequested(); var reference = await _assetService.GetAssetAsync <MetadataReference>(checksum, cancellationToken).ConfigureAwait(false); metadata.Add(reference); } var analyzers = new List <AnalyzerReference>(); foreach (var checksum in projectSnapshot.AnalyzerReferences) { cancellationToken.ThrowIfCancellationRequested(); var reference = await _assetService.GetAssetAsync <AnalyzerReference>(checksum, cancellationToken).ConfigureAwait(false); analyzers.Add(reference); } var additionals = new List <DocumentInfo>(); foreach (var documentChecksum in projectSnapshot.AdditionalDocuments) { cancellationToken.ThrowIfCancellationRequested(); var documentSnapshot = await _assetService.GetAssetAsync <DocumentStateChecksums>(documentChecksum, cancellationToken).ConfigureAwait(false); var documentInfo = await _assetService.GetAssetAsync <SerializedDocumentInfo>(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); var textLoader = TextLoader.From( TextAndVersion.Create( await _assetService.GetAssetAsync <SourceText>(documentSnapshot.Text, cancellationToken).ConfigureAwait(false), VersionStamp.Create(), documentInfo.FilePath)); // TODO: do we need version? additionals.Add( DocumentInfo.Create( documentInfo.Id, documentInfo.Name, documentInfo.Folders, documentInfo.SourceCodeKind, textLoader, documentInfo.FilePath, documentInfo.IsGenerated)); } var compilationOptions = await _assetService.GetAssetAsync <CompilationOptions>(projectSnapshot.CompilationOptions, cancellationToken).ConfigureAwait(false); var parseOptions = await _assetService.GetAssetAsync <ParseOptions>(projectSnapshot.ParseOptions, cancellationToken).ConfigureAwait(false); projects.Add( ProjectInfo.Create( projectInfo.Id, projectInfo.Version, projectInfo.Name, projectInfo.AssemblyName, projectInfo.Language, projectInfo.FilePath, projectInfo.OutputFilePath, compilationOptions, parseOptions, documents, p2p, metadata, analyzers, additionals, projectInfo.IsSubmission)); } return(workspace.AddSolution(SolutionInfo.Create(solutionInfo.Id, solutionInfo.Version, solutionInfo.FilePath, projects))); }