| File: ExternalAccess\UnitTesting\SolutionCrawler\UnitTestingWorkCoordinator.UnitTestingAsyncDocumentWorkItemQueue.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; internal sealed partial class UnitTestingSolutionCrawlerRegistrationService { internal sealed partial class UnitTestingWorkCoordinator { private sealed class UnitTestingAsyncDocumentWorkItemQueue(UnitTestingSolutionCrawlerProgressReporter progressReporter) : UnitTestingAsyncWorkItemQueue<DocumentId>(progressReporter) { private readonly Dictionary<ProjectId, Dictionary<DocumentId, UnitTestingWorkItem>> _documentWorkQueue = []; protected override int WorkItemCount_NoLock => _documentWorkQueue.Count; protected override bool TryTake_NoLock(DocumentId key, out UnitTestingWorkItem workInfo) { workInfo = default; if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && documentMap.TryGetValue(key, out workInfo)) { documentMap.Remove(key); if (documentMap.Count == 0) { _documentWorkQueue.Remove(key.ProjectId); SharedPools.BigDefault<Dictionary<DocumentId, UnitTestingWorkItem>>().ClearAndFree(documentMap); } return true; } return false; } protected override bool TryTakeAnyWork_NoLock( ProjectId? preferableProjectId, out UnitTestingWorkItem workItem) { // there must be at least one item in the map when this is called unless host is shutting down. if (_documentWorkQueue.Count == 0) { workItem = default; return false; } var documentId = GetBestDocumentId_NoLock(preferableProjectId); if (TryTake_NoLock(documentId, out workItem)) { return true; } throw ExceptionUtilities.Unreachable(); } private DocumentId GetBestDocumentId_NoLock( ProjectId? preferableProjectId) { var projectId = GetBestProjectId_NoLock( _documentWorkQueue, preferableProjectId); var documentMap = _documentWorkQueue[projectId]; // explicitly iterate so that we can use struct enumerator. // Return the first normal priority work item we find. If we don't // find any, then just return the first low prio item we saw. DocumentId? lowPriorityDocumentId = null; foreach (var (documentId, workItem) in documentMap) { if (workItem.IsLowPriority) { lowPriorityDocumentId = documentId; } else { return documentId; } } Contract.ThrowIfNull(lowPriorityDocumentId); return lowPriorityDocumentId; } protected override bool AddOrReplace_NoLock(UnitTestingWorkItem item) { Contract.ThrowIfNull(item.DocumentId); Cancel_NoLock(item.DocumentId); // see whether we need to update var key = item.DocumentId; // now document work if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && documentMap.TryGetValue(key, out var existingWorkItem)) { // TODO: should I care about language when replace it? Debug.Assert(existingWorkItem.Language == item.Language); // replace it documentMap[key] = existingWorkItem.With(item.InvocationReasons, item.ActiveMember, item.SpecificAnalyzers, item.IsRetry, item.AsyncToken); return false; } // add document map if it is not already there if (documentMap == null) { documentMap = SharedPools.BigDefault<Dictionary<DocumentId, UnitTestingWorkItem>>().AllocateAndClear(); _documentWorkQueue.Add(key.ProjectId, documentMap); if (_documentWorkQueue.Count == 1) { Logger.Log(FunctionId.WorkCoordinator_AsyncWorkItemQueue_FirstItem); } } // okay, it is new one // always hold onto the most recent one for the same document documentMap.Add(key, item); return true; } protected override void Dispose_NoLock() { foreach (var map in _documentWorkQueue.Values) { foreach (var workItem in map.Values) { workItem.AsyncToken.Dispose(); } SharedPools.BigDefault<Dictionary<DocumentId, UnitTestingWorkItem>>().ClearAndFree(map); } _documentWorkQueue.Clear(); } } } }