public void PostCacheRequest(CacheRequest cacheRequest) { Task.Run(async() => { try { (CacheResult cacheResult, int projectContextId) = await ProcessCacheRequest(cacheRequest); _buildManager.PostCacheResult(cacheRequest, cacheResult, projectContextId); } catch (Exception e) { _buildManager.PostCacheResult(cacheRequest, CacheResult.IndicateException(e), BuildEventContext.InvalidProjectContextId); } }, _cancellationToken); async Task <(CacheResult Result, int ProjectContextId)> ProcessCacheRequest(CacheRequest request) { // Prevent needless evaluation if design time builds detected. if (_projectCacheDescriptor.VsWorkaround && DesignTimeBuildsDetected) { // The BuildManager should disable the cache when it finds its servicing design time builds. return(CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss), BuildEventContext.InvalidProjectContextId); } EvaluateProjectIfNecessary(request); // Detect design time builds. if (_projectCacheDescriptor.VsWorkaround) { var isDesignTimeBuild = IsDesignTimeBuild(request.Configuration.Project); var previousValue = Interlocked.CompareExchange( ref DesignTimeBuildsDetected, new NullableBool(isDesignTimeBuild), null); ErrorUtilities.VerifyThrowInternalError( previousValue is null || previousValue == false || isDesignTimeBuild, "Either all builds in a build session or design time builds, or none"); // No point progressing with expensive plugin initialization or cache query if design time build detected. if (DesignTimeBuildsDetected) { // The BuildManager should disable the cache when it finds its servicing design time builds. return(CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss), BuildEventContext.InvalidProjectContextId); } } // TODO: remove after we change VS to set the cache descriptor via build parameters. // VS workaround needs to wait until the first project is evaluated to extract enough information to initialize the plugin. // No cache request can progress until late initialization is complete. if (_projectCacheDescriptor.VsWorkaround) { if (Interlocked.CompareExchange( ref LateInitializationForVSWorkaroundCompleted, new TaskCompletionSource <bool>(), null) is null) { await LateInitializePluginForVsWorkaround(request); LateInitializationForVSWorkaroundCompleted.SetResult(true); } else { // Can't be null. If the thread got here it means another thread initialized the completion source. await LateInitializationForVSWorkaroundCompleted !.Task; } } ErrorUtilities.VerifyThrowInternalError( LateInitializationForVSWorkaroundCompleted is null || (_projectCacheDescriptor.VsWorkaround && LateInitializationForVSWorkaroundCompleted.Task.IsCompleted), "Completion source should be null when this is not the VS workaround"); BuildRequestData buildRequest = new BuildRequestData( cacheRequest.Configuration.Project, cacheRequest.Submission.BuildRequestData.TargetNames.ToArray()); BuildEventContext buildEventContext = _loggingService.CreateProjectCacheBuildEventContext( cacheRequest.Submission.SubmissionId, evaluationId: cacheRequest.Configuration.Project.EvaluationId, projectInstanceId: cacheRequest.Configuration.ConfigurationId, projectFile: cacheRequest.Configuration.Project.FullPath); CacheResult cacheResult; try { cacheResult = await GetCacheResultAsync(buildRequest, cacheRequest.Configuration, buildEventContext); } catch (Exception ex) { // Wrap the exception here so we can preserve the ProjectContextId cacheResult = CacheResult.IndicateException(ex); } return(cacheResult, buildEventContext.ProjectContextId); }