├── .prettierrc.yaml ├── azure-pipelines ├── no_strongname.txt ├── no_authenticode.txt ├── BuildStageVariables.yml ├── NuGetSbom.props ├── release-deployment-prep.yml ├── GlobalVariables.yml ├── TSAOptions.json ├── microbuild.after.yml ├── vs-insertion-script.ps1 ├── PoliCheckExclusions.xml ├── Get-InsertionPRId.ps1 ├── microbuild.before.yml ├── publish-codecoverage.yml ├── WIFtoPATauth.yml ├── install-dependencies.yml ├── publish_artifacts.ps1 └── PostPRMessage.ps1 ├── docfx ├── .gitignore ├── images │ ├── fsa.png │ ├── blocked_time.png │ ├── blocked_time_callers.png │ ├── vs_threadpoolstarvation_event.jpg │ └── cpu_stacks_showing_threadpool_starvation.png ├── toc.yml ├── docs │ ├── toc.yml │ ├── getting-started.md │ ├── features.md │ └── testing_vs.md ├── index.md ├── analyzers │ ├── fsa.md │ ├── toc.yml │ ├── VSTHRD107.md │ ├── VSTHRD114.md │ ├── VSTHRD109.md │ ├── VSTHRD012.md │ ├── VSTHRD004.md │ ├── VSTHRD104.md │ ├── VSTHRD106.md │ ├── VSTHRD103.md │ ├── VSTHRD200.md │ ├── VSTHRD113.md │ ├── VSTHRD002.md │ ├── VSTHRD102.md │ ├── VSTHRD100.md │ ├── VSTHRD115.md │ ├── VSTHRD108.md │ └── VSTHRD011.md └── docfx.json ├── tools ├── variables │ ├── ShouldSkipOptimize.ps1 │ ├── SymbolsFeatureName.ps1 │ ├── BusinessGroupName.ps1 │ ├── InsertTargetBranch.ps1 │ ├── VstsDropNames.ps1 │ ├── DotNetSdkVersion.ps1 │ ├── LocLanguages.ps1 │ ├── ProfilingInputsPropsName.ps1 │ ├── ProfilingInputsDropName.ps1 │ ├── InsertVersionsValues.ps1 │ ├── InsertPropsValues.ps1 │ ├── _all.ps1 │ ├── InsertJsonValues.ps1 │ └── _define.ps1 ├── dirs.proj ├── artifacts │ ├── projectAssetsJson.ps1 │ ├── test_symbols.ps1 │ ├── build_logs.ps1 │ ├── symbols.ps1 │ ├── testResults.ps1 │ ├── APIScanInputs.ps1 │ ├── LocBin.ps1 │ ├── deployables.ps1 │ ├── coverageResults.ps1 │ ├── VSInsertion.ps1 │ └── Variables.ps1 ├── Get-TempToolsPath.ps1 ├── Get-ArtifactsStagingDirectory.ps1 ├── Get-ProcDump.ps1 ├── Get-NuGetTool.ps1 ├── Get-LibTemplateBasis.ps1 ├── publish-CodeCov.ps1 ├── Check-DotNetSdk.ps1 ├── Check-DotNetRuntime.ps1 ├── Prepare-Legacy-Symbols.ps1 └── Convert-PDB.ps1 ├── .github ├── .editorconfig ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── renovate.json ├── copilot-instructions.md └── workflows │ ├── docs_validate.yml │ ├── docs.yml │ └── copilot-setup-steps.yml ├── CodeQL.yml ├── test ├── Microsoft.VisualStudio.Threading.Tests │ ├── .gitignore │ ├── xunit.runner.json │ ├── .editorconfig │ ├── Usings.cs │ ├── AssemblyInfo.cs │ ├── App.config │ ├── ValidityTests.cs │ ├── GenericParameterHelper.cs │ ├── ReentrantSemaphoreNonJTFTests.cs │ ├── TestUtilitiesTests.cs │ ├── AssertEx.cs │ └── InternalUtilitiesTests.cs ├── Microsoft.VisualStudio.Threading.Analyzers.Tests │ ├── AdditionalFiles │ │ ├── vs-threading.MainThreadSwitchingMethods.mocks.txt │ │ ├── vs-threading.SyncMethodsToExcludeFromVSTHRD103.mocks.txt │ │ ├── vs-threading.MainThreadAssertingMethods.mocks.txt │ │ ├── vs-threading.LegacyThreadSwitchingMembers.mocks.txt │ │ └── vs-threading.MembersRequiringMainThread.mocks.txt │ ├── xunit.runner.json │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Usings.cs │ ├── VSTHRD100AsyncVoidMethodCodeFixTests.cs │ └── Helpers │ │ ├── CSharpCodeFixVerifier`2.cs │ │ └── VisualBasicCodeFixVerifier`2.cs ├── NativeAOTCompatibility.Test │ ├── Program.cs │ └── NativeAOTCompatibility.Test.csproj ├── IsolatedTestHost │ ├── IsolatedTestHost.csproj │ ├── App.config │ ├── TestOutputHelper.cs │ └── ExitCode.cs ├── Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher │ ├── App.config │ ├── Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj │ └── Program.cs ├── dirs.proj ├── Directory.Build.props ├── AOT.props ├── Directory.Build.targets └── Directory.Packages.props ├── settings.VisualStudio.json ├── src ├── PackageIcon.png ├── Microsoft.VisualStudio.Threading │ ├── NativeMethods.txt │ ├── GlobalSuppressions.cs │ ├── CustomDictionary.xml │ ├── Reasons.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── EmptyStruct.cs │ ├── SemaphoreFaultedException.cs │ ├── IAsyncDisposable.cs │ ├── IHangReportContributor.cs │ ├── IllegalSemaphoreUsageException.cs │ ├── Boxed.cs │ ├── RoslynDebug.cs │ ├── IPendingExecutionRequestState.cs │ ├── JoinableTaskCreationOptions.cs │ ├── AsyncEventHandler.cs │ ├── AsyncLocal`1.cs │ ├── LightUps.cs │ ├── README.md │ ├── RegistryChangeNotificationFilters.cs │ ├── InternalUtilities.cs │ ├── NullableHelpers.cs │ ├── JoinableTaskInternals.cs │ ├── IJoinableTaskDependent.cs │ └── JoinableTaskContextException.cs ├── Microsoft.VisualStudio.Threading.Analyzers.CodeFixes │ ├── buildTransitive │ │ ├── AdditionalFiles │ │ │ ├── vs-threading.MembersRequiringMainThread.txt │ │ │ ├── vs-threading.MainThreadAssertingMethods.txt │ │ │ ├── vs-threading.MainThreadSwitchingMethods.txt │ │ │ └── vs-threading.LegacyThreadSwitchingMembers.txt │ │ └── Microsoft.VisualStudio.Threading.Analyzers.targets │ ├── version.json │ ├── README.md │ ├── AssemblyInfo.cs │ ├── NullableHelpers.cs │ └── tools │ │ ├── install.ps1 │ │ └── uninstall.ps1 ├── dirs.proj ├── Microsoft.VisualStudio.Threading.Analyzers │ ├── Usings.cs │ ├── version.json │ ├── AssemblyInfo.cs │ ├── Microsoft.VisualStudio.Threading.Analyzers.csproj │ └── LanguageUtils.cs ├── Microsoft.VisualStudio.Threading.Analyzers.CSharp │ ├── version.json │ ├── AssemblyInfo.cs │ ├── CSharpVSTHRD011UseAsyncLazyAnalyzer.cs │ ├── CSharpVSTHRD012SpecifyJtfWhereAllowed.cs │ ├── CSharpVSTHRD114AvoidReturningNullTaskAnalyzer.cs │ ├── CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs │ ├── CSharpVSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs │ ├── CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs │ ├── CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs │ ├── CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs │ ├── CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs │ ├── CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs │ └── Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj ├── Microsoft.VisualStudio.Threading.Analyzers.VisualBasic │ ├── version.json │ ├── AssemblyInfo.cs │ ├── VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs │ ├── VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs │ ├── VisualBasicVSTHRD114AvoidReturningNullTaskAnalyzer.cs │ ├── VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs │ ├── VisualBasicVSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs │ ├── VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs │ ├── VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs │ ├── VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs │ ├── VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs │ ├── VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs │ └── Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj ├── AssemblyInfo.cs ├── AssemblyInfo.vb ├── SosThreadingTools │ ├── ICommandHandler.cs │ ├── GalleryManifest.xml │ ├── ExtensionContext.cs │ ├── Commands.cs │ └── SosThreadingTools.csproj ├── Directory.Build.targets ├── .editorconfig ├── OptProf.targets ├── LibraryNuspecProperties.props ├── Directory.Build.props └── Microsoft.VisualStudio.Threading.JointPackage │ └── Microsoft.VisualStudio.Threading.JointPackage.csproj ├── doc ├── editorconfigs │ ├── SharedLibrary.editorconfig │ ├── Tests.editorconfig │ ├── JTFFocusedLibrary.editorconfig │ ├── AppWithMainThread.editorconfig │ └── AppWithoutMainThread.editorconfig └── analyzers │ ├── VSTHRD001.md │ ├── VSTHRD002.md │ ├── VSTHRD003.md │ ├── VSTHRD004.md │ ├── VSTHRD010.md │ ├── VSTHRD011.md │ ├── VSTHRD012.md │ ├── VSTHRD100.md │ ├── VSTHRD101.md │ ├── VSTHRD102.md │ ├── VSTHRD103.md │ ├── VSTHRD104.md │ ├── VSTHRD105.md │ ├── VSTHRD106.md │ ├── VSTHRD107.md │ ├── VSTHRD108.md │ ├── VSTHRD109.md │ ├── VSTHRD110.md │ ├── VSTHRD111.md │ ├── VSTHRD112.md │ ├── VSTHRD113.md │ ├── VSTHRD114.md │ ├── VSTHRD115.md │ └── VSTHRD200.md ├── .azuredevops └── dependabot.yml ├── loc └── lci │ ├── SosThreadingToolsManaged.dll.lci │ ├── Microsoft.VisualStudio.Threading.dll.lci │ └── Microsoft.VisualStudio.Threading.Analyzers.dll.lci ├── version.json ├── global.json ├── azurepipelines-coverage.yml ├── Directory.Build.targets ├── .vscode ├── tasks.json ├── launch.json ├── extensions.json └── settings.json ├── nuget.config ├── init.cmd ├── stylecop.json ├── azure-pipelines.yml ├── Directory.Build.rsp ├── .config └── dotnet-tools.json ├── SUPPORT.md ├── Directory.Traversal.targets ├── LICENSE ├── samples ├── samples.csproj ├── ApiSamples.cs └── .editorconfig └── README.md /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /azure-pipelines/no_strongname.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docfx/.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | api/ 3 | -------------------------------------------------------------------------------- /tools/variables/ShouldSkipOptimize.ps1: -------------------------------------------------------------------------------- 1 | 'false' 2 | -------------------------------------------------------------------------------- /tools/variables/SymbolsFeatureName.ps1: -------------------------------------------------------------------------------- 1 | 'MS.VS.Threading' 2 | -------------------------------------------------------------------------------- /.github/.editorconfig: -------------------------------------------------------------------------------- 1 | [renovate.json*] 2 | indent_style = tab 3 | -------------------------------------------------------------------------------- /CodeQL.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | library: 3 | - 'test/**' 4 | -------------------------------------------------------------------------------- /tools/variables/BusinessGroupName.ps1: -------------------------------------------------------------------------------- 1 | 'Visual Studio - VS Core' 2 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/.gitignore: -------------------------------------------------------------------------------- 1 | FakesAssemblies 2 | Fakes 3 | -------------------------------------------------------------------------------- /settings.VisualStudio.json: -------------------------------------------------------------------------------- 1 | { 2 | "textEditor.codeCleanup.profile": "profile1" 3 | } 4 | -------------------------------------------------------------------------------- /docfx/images/fsa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/docfx/images/fsa.png -------------------------------------------------------------------------------- /src/PackageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/src/PackageIcon.png -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/NativeMethods.txt: -------------------------------------------------------------------------------- 1 | WaitForMultipleObjects 2 | RegNotifyChangeKeyValue 3 | -------------------------------------------------------------------------------- /docfx/images/blocked_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/docfx/images/blocked_time.png -------------------------------------------------------------------------------- /docfx/images/blocked_time_callers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/docfx/images/blocked_time_callers.png -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/vs-threading.MembersRequiringMainThread.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/variables/InsertTargetBranch.ps1: -------------------------------------------------------------------------------- 1 | # This is the default branch of the VS repo that we will use to insert into VS. 2 | 'main' 3 | -------------------------------------------------------------------------------- /doc/editorconfigs/SharedLibrary.editorconfig: -------------------------------------------------------------------------------- 1 | # VSTHRD111: Use .ConfigureAwait(bool) 2 | dotnet_diagnostic.VSTHRD111.severity = warning 3 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD001.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD001.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD002.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD002.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD003.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD003.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD004.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD004.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD010.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD010.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD011.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD011.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD012.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD012.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD100.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD100.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD101.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD101.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD102.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD102.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD103.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD103.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD104.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD104.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD105.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD105.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD106.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD106.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD107.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD107.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD108.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD108.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD109.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD109.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD110.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD110.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD111.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD111.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD112.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD112.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD113.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD113.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD114.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD114.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD115.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD115.html). 2 | -------------------------------------------------------------------------------- /doc/analyzers/VSTHRD200.md: -------------------------------------------------------------------------------- 1 | This content has been moved to [GitHub Pages](https://microsoft.github.io/vs-threading/analyzers/VSTHRD200.html). 2 | -------------------------------------------------------------------------------- /tools/variables/VstsDropNames.ps1: -------------------------------------------------------------------------------- 1 | "Products/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 2 | -------------------------------------------------------------------------------- /docfx/images/vs_threadpoolstarvation_event.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/docfx/images/vs_threadpoolstarvation_event.jpg -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.mocks.txt: -------------------------------------------------------------------------------- 1 | [Test]::MySwitchingMethodAsync 2 | -------------------------------------------------------------------------------- /tools/variables/DotNetSdkVersion.ps1: -------------------------------------------------------------------------------- 1 | $globalJson = Get-Content -LiteralPath "$PSScriptRoot\..\..\global.json" | ConvertFrom-Json 2 | $globalJson.sdk.version 3 | -------------------------------------------------------------------------------- /docfx/images/cpu_stacks_showing_threadpool_starvation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/docfx/images/cpu_stacks_showing_threadpool_starvation.png -------------------------------------------------------------------------------- /azure-pipelines/no_authenticode.txt: -------------------------------------------------------------------------------- 1 | bin\packages\release\vsix\_manifest\manifest.cat,sbom signed 2 | bin\packages\release\vsix\_manifest\spdx_2.2\manifest.cat,sbom signed 3 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-threading/HEAD/src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs -------------------------------------------------------------------------------- /src/dirs.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/vs-threading.MainThreadAssertingMethods.txt: -------------------------------------------------------------------------------- 1 | [System.Windows.Threading.Dispatcher]::VerifyAccess 2 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "shadowCopy": false 4 | } 5 | -------------------------------------------------------------------------------- /doc/editorconfigs/Tests.editorconfig: -------------------------------------------------------------------------------- 1 | # These settings are appropriate for test projects. 2 | 3 | # VSTHRD200: Use `Async` naming convention 4 | dotnet_diagnostic.VSTHRD200.severity = none 5 | -------------------------------------------------------------------------------- /azure-pipelines/BuildStageVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 3 | BuildConfiguration: Release 4 | NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ 5 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.SyncMethodsToExcludeFromVSTHRD103.mocks.txt: -------------------------------------------------------------------------------- 1 | # Test exclusions for VSTHRD103 analyzer 2 | [TestNamespace.TestClass]::SlowSyncMethod -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "shadowCopy": false, 4 | "methodDisplay": "method" 5 | } 6 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.txt: -------------------------------------------------------------------------------- 1 | [Microsoft.VisualStudio.Threading.JoinableTaskFactory]::SwitchToMainThreadAsync 2 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadAssertingMethods.mocks.txt: -------------------------------------------------------------------------------- 1 | [Test]::VerifyOnUIThread 2 | [Microsoft.VisualStudio.Shell.ThreadHelper]::ThrowIfNotOnUIThread 3 | -------------------------------------------------------------------------------- /docfx/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Docs 3 | href: docs/ 4 | - name: Analyzers 5 | href: analyzers/ 6 | - name: API 7 | href: api/ 8 | - name: GitHub 9 | href: https://github.com/microsoft/vs-threading 10 | -------------------------------------------------------------------------------- /tools/variables/LocLanguages.ps1: -------------------------------------------------------------------------------- 1 | ## For faster PR/CI builds localize only for 2 languages, ENU and JPN provide good enough coverage 2 | if ($env:BUILD_REASON -eq 'PullRequest') { 3 | 'ENU,JPN' 4 | } else { 5 | 'VS' 6 | } 7 | -------------------------------------------------------------------------------- /azure-pipelines/NuGetSbom.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 2 5 | 6 | 7 | -------------------------------------------------------------------------------- /tools/dirs.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /doc/editorconfigs/JTFFocusedLibrary.editorconfig: -------------------------------------------------------------------------------- 1 | # These settings are appropriate for libraries that always run within a process that follows the JoinableTaskFactory rules. 2 | 3 | # VSTHRD111: Use .ConfigureAwait(bool) 4 | dotnet_diagnostic.VSTHRD111.severity = silent 5 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers/Usings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | global using System.Collections.Immutable; 5 | -------------------------------------------------------------------------------- /tools/artifacts/projectAssetsJson.ps1: -------------------------------------------------------------------------------- 1 | $ObjRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\obj") 2 | 3 | if (!(Test-Path $ObjRoot)) { return } 4 | 5 | @{ 6 | "$ObjRoot" = ( 7 | (Get-ChildItem "$ObjRoot\project.assets.json" -Recurse) 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /tools/artifacts/test_symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | if (!(Test-Path $BinPath)) { return } 3 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique 4 | 5 | @{ 6 | "$BinPath" = $SymbolFiles; 7 | } 8 | -------------------------------------------------------------------------------- /azure-pipelines/release-deployment-prep.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - download: CI 3 | artifact: Variables-Windows 4 | displayName: 🔻 Download Variables-Windows artifact 5 | - powershell: $(Pipeline.Workspace)/CI/Variables-Windows/_define.ps1 6 | displayName: ⚙️ Set pipeline variables based on artifacts 7 | -------------------------------------------------------------------------------- /doc/editorconfigs/AppWithMainThread.editorconfig: -------------------------------------------------------------------------------- 1 | # This file applies to applications with a main thread, and libraries that may run in them. 2 | 3 | # This file is intentionally empty because the default severity levels for the threading analyzers 4 | # are optimized for these types of projects. 5 | -------------------------------------------------------------------------------- /docfx/docs/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - href: getting-started.md 3 | - href: features.md 4 | - href: threading_rules.md 5 | - href: cookbook_vs.md 6 | - href: testing_vs.md 7 | - href: library_with_jtf.md 8 | - href: threadpool_starvation.md 9 | - href: async_hang.md 10 | - href: dumpasync.md 11 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "inherit": true, 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.mocks.txt: -------------------------------------------------------------------------------- 1 | [Microsoft.VisualStudio.Shell.ThreadHelper]::Invoke 2 | [Microsoft.VisualStudio.Shell.ThreadHelper]::InvokeAsync 3 | [Microsoft.VisualStudio.Shell.ThreadHelper]::BeginInvoke 4 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | # xUnit1030: Test methods should not call ConfigureAwait(false) 3 | dotnet_diagnostic.xUnit1030.severity = none 4 | # xUnit1031: Test methods should not use blocking task operations 5 | dotnet_diagnostic.xUnit1031.severity = none 6 | -------------------------------------------------------------------------------- /.azuredevops/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://eng.ms/docs/products/dependabot/configuration/version_updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: nuget 7 | directory: / 8 | schedule: 9 | interval: monthly 10 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "inherit": true, 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | global using Microsoft.VisualStudio.Threading; 5 | global using Xunit; 6 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "inherit": true, 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "inherit": true, 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tools/artifacts/build_logs.ps1: -------------------------------------------------------------------------------- 1 | $ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 2 | 3 | if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } 4 | 5 | @{ 6 | "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") 7 | } 8 | -------------------------------------------------------------------------------- /docfx/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # Overview 6 | 7 | This is the documentation site for the Microsoft.VisualStudio.Threading library and analyzers. 8 | 9 | Click the Docs or Analyzers heading on the top to read the docs. 10 | Or click the API heading to see API-level documentation. 11 | -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] 7 | -------------------------------------------------------------------------------- /src/AssemblyInfo.vb: -------------------------------------------------------------------------------- 1 | ' Copyright (c) Microsoft Corporation. All rights reserved. 2 | ' Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | Imports System.Runtime.InteropServices 5 | 6 | 7 | -------------------------------------------------------------------------------- /tools/variables/ProfilingInputsPropsName.ps1: -------------------------------------------------------------------------------- 1 | if ($env:SYSTEM_TEAMPROJECT) { 2 | $repoName = $env:BUILD_REPOSITORY_NAME.Replace('/', '.') 3 | "$env:SYSTEM_TEAMPROJECT.$repoName.props" 4 | } else { 5 | Write-Warning "No Azure Pipelines build detected. No Azure Pipelines drop name will be computed." 6 | } 7 | -------------------------------------------------------------------------------- /tools/variables/ProfilingInputsDropName.ps1: -------------------------------------------------------------------------------- 1 | if ($env:SYSTEM_TEAMPROJECT) { 2 | "ProfilingInputs/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 3 | } else { 4 | Write-Warning "No Azure Pipelines build detected. No Azure Pipelines drop name will be computed." 5 | } 6 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft.VisualStudio.Threading.Analyzers 2 | 3 | Static code analyzers to detect common mistakes or potential issues regarding threading and async coding. 4 | 5 | [Diagnostic analyzer rules](https://microsoft.github.io/vs-threading/analyzers/index.html). 6 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: ComVisible(false)] 7 | -------------------------------------------------------------------------------- /src/SosThreadingTools/ICommandHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace CpsDbg; 5 | 6 | internal interface ICommandHandler 7 | { 8 | void Execute(string args); 9 | } 10 | -------------------------------------------------------------------------------- /azure-pipelines/GlobalVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | # These variables are required for MicroBuild tasks 3 | TeamName: VS Core - Special Projects 4 | TeamEmail: andarno@microsoft.com 5 | # These variables influence insertion pipelines 6 | ContainsVsix: false # This should be true when the repo builds a VSIX that should be inserted to VS. 7 | -------------------------------------------------------------------------------- /test/NativeAOTCompatibility.Test/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | System.Console.WriteLine("This test is run by \"dotnet publish -c release -r [RID]-x64\" rather than by executing the program."); 5 | -------------------------------------------------------------------------------- /loc/lci/SosThreadingToolsManaged.dll.lci: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/CustomDictionary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | awaiter 6 | awaiters 7 | awaitable 8 | dequeued 9 | enqueued 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /loc/lci/Microsoft.VisualStudio.Threading.dll.lci: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "18.0", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main$", 6 | "^refs/heads/v\\d+(?:\\.\\d+)?$" 7 | ], 8 | "cloudBuild": { 9 | "setVersionVariables": false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tools/variables/InsertVersionsValues.ps1: -------------------------------------------------------------------------------- 1 | $MacroName = 'MicrosoftVisualStudioThreadingVersion' 2 | $SampleProject = "$PSScriptRoot\..\..\src\Microsoft.VisualStudio.Threading" 3 | [string]::join(',',(@{ 4 | ($MacroName) = & { (dotnet nbgv get-version --project $SampleProject --format json | ConvertFrom-Json).AssemblyVersion }; 5 | }.GetEnumerator() |% { "$($_.key)=$($_.value)" })) 6 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "10.0.101", 4 | "rollForward": "patch", 5 | "allowPrerelease": false 6 | }, 7 | "test": { 8 | "runner": "Microsoft.Testing.Platform" 9 | }, 10 | "msbuild-sdks": { 11 | "MSBuild.Sdk.Extras": "3.0.44", 12 | "Microsoft.Build.NoTargets": "3.7.134", 13 | "Microsoft.Build.Traversal": "4.1.82" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /loc/lci/Microsoft.VisualStudio.Threading.Analyzers.dll.lci: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.txt: -------------------------------------------------------------------------------- 1 | [System.Windows.Threading.Dispatcher]::Invoke 2 | [System.Windows.Threading.Dispatcher]::InvokeAsync 3 | [System.Windows.Threading.Dispatcher]::BeginInvoke 4 | [System.Threading.SynchronizationContext]::Send 5 | [System.Threading.SynchronizationContext]::Post 6 | -------------------------------------------------------------------------------- /azurepipelines-coverage.yml: -------------------------------------------------------------------------------- 1 | # https://learn.microsoft.com/azure/devops/pipelines/test/codecoverage-for-pullrequests 2 | coverage: 3 | status: 4 | comments: on # add comment to PRs reporting diff in coverage of modified files 5 | diff: # diff coverage is code coverage only for the lines changed in a pull request. 6 | target: 70% # set this to a desired %. Default is 70% 7 | -------------------------------------------------------------------------------- /docfx/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Installation 4 | 5 | Consume this library via its NuGet Package. 6 | Click on the badge to find its latest version and the instructions for consuming it that best apply to your project. 7 | 8 | [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Threading.svg)](https://www.nuget.org/packages/Microsoft.VisualStudio.Threading) 9 | -------------------------------------------------------------------------------- /tools/Get-TempToolsPath.ps1: -------------------------------------------------------------------------------- 1 | if ($env:AGENT_TEMPDIRECTORY) { 2 | $path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID" 3 | } elseif ($env:localappdata) { 4 | $path = "$env:localappdata\gitrepos\tools" 5 | } else { 6 | $path = "$PSScriptRoot\..\obj\tools" 7 | } 8 | 9 | if (!(Test-Path $path)) { 10 | New-Item -ItemType Directory -Path $Path | Out-Null 11 | } 12 | 13 | (Resolve-Path $path).Path 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/Microsoft.VisualStudio.Threading.Analyzers.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/IsolatedTestHost/IsolatedTestHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net472 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Some of our tests measure stress, GC pressure, etc. 5 | // It messes with reliable test results when other threads are doing random stuff. 6 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 7 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /doc/editorconfigs/AppWithoutMainThread.editorconfig: -------------------------------------------------------------------------------- 1 | # These settings are appropriate for most projects that *never* run inside a process with a main thread and `SynchronizationContext`. 2 | # Examples of such applications are ASP.NET Core and console applications. 3 | 4 | # VSTHRD012: Provide JoinableTaskFactory where allowed 5 | dotnet_diagnostic.VSTHRD012.severity = none 6 | 7 | # VSTHRD003: Avoid awaiting foreign Tasks 8 | dotnet_diagnostic.VSTHRD003.severity = none 9 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | global using System.Threading.Tasks; 5 | global using Microsoft; 6 | global using Microsoft.CodeAnalysis; 7 | global using Microsoft.CodeAnalysis.Testing; 8 | global using Microsoft.VisualStudio.Threading.Analyzers; 9 | global using Xunit; 10 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tools/artifacts/symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | $ExternalPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../obj/SymbolsPackages") 3 | if (!(Test-Path $BinPath)) { return } 4 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique 5 | $ExternalFiles = & "$PSScriptRoot/../Get-ExternalSymbolFiles.ps1" 6 | 7 | @{ 8 | "$BinPath" = $SymbolFiles; 9 | "$ExternalPath" = $ExternalFiles; 10 | } 11 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Resources; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | [assembly: CLSCompliant(false)] 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/Reasons.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft.VisualStudio.Threading; 5 | 6 | internal static class Reasons 7 | { 8 | internal const string DiagnosticAnalysisOnly = "This API performs analysis of runtime objects for diagnostic purposes only, and isn't required for correct functionality."; 9 | } 10 | -------------------------------------------------------------------------------- /tools/variables/InsertPropsValues.ps1: -------------------------------------------------------------------------------- 1 | $InsertedPkgs = (& "$PSScriptRoot\..\artifacts\VSInsertion.ps1") 2 | 3 | $icv=@() 4 | foreach ($kvp in $InsertedPkgs.GetEnumerator()) { 5 | $kvp.Value |% { 6 | if ($_.Name -match "^(.*?)\.(\d+\.\d+\.\d+(?:\.\d+)?(?:-.*?)?)(?:\.symbols)?\.nupkg$") { 7 | $id = $Matches[1] 8 | $version = $Matches[2] 9 | $icv += "$id=$version" 10 | } 11 | } 12 | } 13 | 14 | Write-Output ([string]::join(',',$icv)) 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Resources; 6 | using System.Runtime.InteropServices; 7 | 8 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] 9 | 10 | [assembly: CLSCompliant(true)] 11 | [assembly: ComVisible(false)] 12 | -------------------------------------------------------------------------------- /tools/variables/_all.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | This script returns a hashtable of build variables that should be set 6 | at the start of a build or release definition's execution. 7 | #> 8 | 9 | [CmdletBinding(SupportsShouldProcess = $true)] 10 | param ( 11 | ) 12 | 13 | $vars = @{} 14 | 15 | Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" |% { 16 | Write-Host "Computing $($_.BaseName) variable" 17 | $vars[$_.BaseName] = & $_ 18 | } 19 | 20 | $vars 21 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Resources; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | [assembly: CLSCompliant(false)] 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Resources; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | [assembly: CLSCompliant(false)] 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Resources; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | [assembly: CLSCompliant(false)] 10 | [assembly: ComVisible(false)] 11 | 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | -------------------------------------------------------------------------------- /test/IsolatedTestHost/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472 4 | Exe 5 | false 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | [Microsoft.VisualStudio.Threading.Analyzers*/*.cs] 2 | 3 | # CS1591: Missing XML comment for publicly visible type or member 4 | dotnet_diagnostic.CS1591.severity = suggestion 5 | 6 | # CA1062: Validate arguments of public methods 7 | dotnet_diagnostic.CA1062.severity = none 8 | 9 | # CA2007: Consider calling ConfigureAwait on the awaited task 10 | dotnet_diagnostic.CA2007.severity = silent 11 | 12 | # RS2008: Enable analyzer release tracking 13 | dotnet_diagnostic.RS2008.severity = suggestion 14 | -------------------------------------------------------------------------------- /test/dirs.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Attach", 9 | "type": "coreclr", 10 | "request": "attach", 11 | "processId": "${command:pickProcess}" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tools/artifacts/testResults.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | ) 4 | 5 | $result = @{} 6 | 7 | $RepoRoot = Resolve-Path "$PSScriptRoot\..\.." 8 | $testRoot = Join-Path $RepoRoot test 9 | $result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) 10 | 11 | $artifactStaging = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 12 | $testlogsPath = Join-Path $artifactStaging "test_logs" 13 | if (Test-Path $testlogsPath) { 14 | $result[$testlogsPath] = Get-ChildItem $testlogsPath -Recurse; 15 | } 16 | 17 | $result 18 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tools/Get-ArtifactsStagingDirectory.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [switch]$CleanIfLocal 3 | ) 4 | if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { 5 | $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY 6 | } elseif ($env:RUNNER_TEMP) { 7 | $ArtifactStagingFolder = Join-Path $env:RUNNER_TEMP _artifacts 8 | } else { 9 | $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") 10 | if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { 11 | Remove-Item $ArtifactStagingFolder -Recurse -Force 12 | } 13 | } 14 | 15 | $ArtifactStagingFolder 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | #### Bug description 8 | A clear and concise description of what the bug is. 9 | 10 | #### Repro steps 11 | Code to reproduce the behavior. 12 | 13 | #### Expected behavior 14 | A clear and concise description of what you expected to happen. 15 | 16 | #### Actual behavior 17 | What happened instead of what you expected. 18 | 19 | - Version used: 20 | - Application (if applicable): 21 | 22 | #### Additional context 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | true 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MembersRequiringMainThread.mocks.txt: -------------------------------------------------------------------------------- 1 | [TestNS.*] 2 | [TestNS.SomeStruct] 3 | ![TestNS.FreeThreadedType] 4 | ![TestNS2.FreeThreadedType] 5 | [TestNS2.*] 6 | [Microsoft.VisualStudio.Shell.Interop.*] 7 | [Microsoft.VisualStudio.OLE.Interop.*] 8 | [Microsoft.Internal.VisualStudio.Shell.Interop.*] 9 | ![Microsoft.VisualStudio.Shell.Interop.IAsyncServiceProvider] 10 | [Microsoft.VisualStudio.Shell.Package]::GetService 11 | [Microsoft.VisualStudio.Shell.ServiceProvider] 12 | ![TestNS2.A]::FreeThreadedMethod 13 | [A]::UIPropertyName 14 | -------------------------------------------------------------------------------- /azure-pipelines/TSAOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsaVersion": "TsaV2", 3 | "codebase": "NewOrUpdate", 4 | "codebaseName": "Microsoft.VisualStudio.Threading", 5 | "tsaStamp": "DevDiv", 6 | "tsaEnvironment": "PROD", 7 | "notificationAliases": [ 8 | "andarno@microsoft.com" 9 | ], 10 | "codebaseAdmins": [ 11 | "REDMOND\\andarno" 12 | ], 13 | "instanceUrl": "https://devdiv.visualstudio.com", 14 | "projectName": "DevDiv", 15 | "areaPath": "DevDiv\\VS Core\\Special Projects\\vs-threading", 16 | "iterationPath": "DevDiv", 17 | "alltools": true, 18 | "repositoryName": "vs-threading" 19 | } 20 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.after.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: SkipCodesignVerify 3 | type: boolean 4 | 5 | steps: 6 | - ${{ if not(parameters.SkipCodesignVerify) }}: 7 | - task: MicroBuildCodesignVerify@3 8 | displayName: 🔍 Verify Signed Files 9 | inputs: 10 | ApprovalListPathForSigs: $(Build.SourcesDirectory)\azure-pipelines\no_strongname.txt 11 | ApprovalListPathForCerts: $(Build.SourcesDirectory)\azure-pipelines\no_authenticode.txt 12 | TargetFolders: | 13 | $(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration) 14 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) 15 | -------------------------------------------------------------------------------- /tools/artifacts/APIScanInputs.ps1: -------------------------------------------------------------------------------- 1 | $inputs = & "$PSScriptRoot/symbols.ps1" 2 | 3 | if (!$inputs) { return } 4 | 5 | # Filter out specific files that target OS's that are not subject to APIScan. 6 | # Files that are subject but are not supported must be scanned and an SEL exception filed. 7 | $outputs = @{} 8 | $forbiddenSubPaths = @( 9 | , 'linux-*' 10 | , 'osx*' 11 | ) 12 | 13 | $inputs.GetEnumerator() | % { 14 | $list = $_.Value | ? { 15 | $path = $_.Replace('\', '/') 16 | return !($forbiddenSubPaths | ? { $path -like "*/$_/*" }) 17 | } 18 | $outputs[$_.Key] = $list 19 | } 20 | 21 | 22 | $outputs 23 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD011UseAsyncLazyAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD011UseAsyncLazyAnalyzer : AbstractVSTHRD011UseAsyncLazyAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD012SpecifyJtfWhereAllowed.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD012SpecifyJtfWhereAllowed : AbstractVSTHRD012SpecifyJtfWhereAllowed 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /tools/artifacts/LocBin.ps1: -------------------------------------------------------------------------------- 1 | # Identify LCE files and the binary files they describe 2 | $BinRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin") 3 | if (!(Test-Path $BinRoot)) { return } 4 | 5 | $FilesToCopy = @() 6 | $FilesToCopy += Get-ChildItem -Recurse -File -Path $BinRoot |? { $_.FullName -match '\\Localize\\' } 7 | 8 | Get-ChildItem -rec "$BinRoot\*.lce" -File | % { 9 | $FilesToCopy += $_ 10 | $FilesToCopy += $_.FullName.SubString(0, $_.FullName.Length - 4) 11 | } 12 | 13 | $FilesToCopy += Get-ChildItem -rec "$BinRoot\*.lcg" -File | % { [xml](Get-Content $_) } | % { $_.lcx.name } 14 | 15 | @{ 16 | "$BinRoot" = $FilesToCopy; 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | #### Is your feature request related to a problem? Please describe. 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | #### Describe the solution you'd like 11 | A clear and concise description of what you want to happen. 12 | 13 | #### Describe alternatives you've considered 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | #### Additional context 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /tools/Get-ProcDump.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed. 4 | #> 5 | $version = '0.0.1' 6 | $baseDir = "$PSScriptRoot\..\obj\tools" 7 | $procDumpToolPath = "$baseDir\procdump.$version\bin" 8 | if (-not (Test-Path $procDumpToolPath)) { 9 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 10 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 11 | & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir | Out-Null 12 | } 13 | 14 | (Resolve-Path $procDumpToolPath).Path 15 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD011UseAsyncLazyAnalyzer : AbstractVSTHRD011UseAsyncLazyAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD114AvoidReturningNullTaskAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD114AvoidReturningNullTaskAnalyzer : AbstractVSTHRD114AvoidReturningNullTaskAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD012SpecifyJtfWhereAllowed : AbstractVSTHRD012SpecifyJtfWhereAllowed 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/OptProf.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IBC 5 | Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Threading.17.x\$(TargetFileName) 6 | /ExeConfig:"%VisualStudio.InstallationUnderTest.Path%\Common7\IDE\vsn.exe" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer : AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD110ObserveResultOfAsyncCallsAnalyzer : AbstractVSTHRD110ObserveResultOfAsyncCallsAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /docfx/analyzers/fsa.md: -------------------------------------------------------------------------------- 1 | # Full Solution Analysis 2 | 3 | Analyzers that require Full Solution Analysis produce diagnostics that may not always be visible in Visual Studio. 4 | 5 | Diagnostics reported by such an analyzer will appear in a full build log. They may also appear in Visual Studio's Error List after a full build. But these diagnostics may disappear when the document they refer to is opened in the editor. 6 | 7 | To ensure the diagnostics are always visible, even when documents are open, select the "Enable full Solution Analysis" option, as shown below: 8 | 9 | ![Visual Studio Options -> Text Editor -> C# -> Advanced -> Enable full solution analysis option](../images/fsa.png) 10 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer : AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer : AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/LibraryNuspecProperties.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net8.0;net472 4 | $(TargetFrameworks);net8.0-windows 5 | 6 | Async synchronization primitives, async collections, TPL and dataflow extensions. 7 | The JoinableTaskFactory allows synchronously blocking the UI thread for async work. This 8 | package is applicable to any .NET application (not just Visual Studio). 9 | 10 | Threading Async Lock Synchronization Threadsafe 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD108AssertThreadRequirementUnconditionally : AbstractVSTHRD108AssertThreadRequirementUnconditionally 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /test/AOT.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | true 5 | true 6 | true 7 | $(AvailableRuntimeIdentifiers) 8 | $(DefaultRuntimeIdentifier) 9 | 10 | 11 | 12 | 13 | RuntimeIdentifier 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD114AvoidReturningNullTaskAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD114AvoidReturningNullTaskAnalyzer : AbstractVSTHRD114AvoidReturningNullTaskAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer : AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer : AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD110ObserveResultOfAsyncCallsAnalyzer : AbstractVSTHRD110ObserveResultOfAsyncCallsAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer : AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer : AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD108AssertThreadRequirementUnconditionally : AbstractVSTHRD108AssertThreadRequirementUnconditionally 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public sealed class CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer : AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => CSharpUtils.Instance; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer : AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /init.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | set PS1UnderCmd=1 4 | 5 | :: Get the datetime in a format that can go in a filename. 6 | set _my_datetime=%date%_%time% 7 | set _my_datetime=%_my_datetime: =_% 8 | set _my_datetime=%_my_datetime::=% 9 | set _my_datetime=%_my_datetime:/=_% 10 | set _my_datetime=%_my_datetime:.=_% 11 | set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd 12 | 13 | powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }" 14 | 15 | :: Set environment variables in the parent cmd.exe process. 16 | IF EXIST "%CmdEnvScriptPath%" ( 17 | ENDLOCAL 18 | CALL "%CmdEnvScriptPath%" 19 | DEL "%CmdEnvScriptPath%" 20 | ) 21 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Microsoft.VisualStudio.Threading.Analyzers; 8 | 9 | [DiagnosticAnalyzer(LanguageNames.VisualBasic)] 10 | public sealed class VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer : AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer 11 | { 12 | protected override LanguageUtils LanguageUtils => VisualBasicUtils.Instance; 13 | } 14 | -------------------------------------------------------------------------------- /docfx/analyzers/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - href: index.md 3 | - href: configuration.md 4 | - href: fsa.md 5 | - href: installation.md 6 | - href: VSTHRD001.md 7 | - href: VSTHRD002.md 8 | - href: VSTHRD003.md 9 | - href: VSTHRD004.md 10 | - href: VSTHRD010.md 11 | - href: VSTHRD011.md 12 | - href: VSTHRD012.md 13 | - href: VSTHRD100.md 14 | - href: VSTHRD101.md 15 | - href: VSTHRD102.md 16 | - href: VSTHRD103.md 17 | - href: VSTHRD104.md 18 | - href: VSTHRD105.md 19 | - href: VSTHRD106.md 20 | - href: VSTHRD107.md 21 | - href: VSTHRD108.md 22 | - href: VSTHRD109.md 23 | - href: VSTHRD110.md 24 | - href: VSTHRD111.md 25 | - href: VSTHRD112.md 26 | - href: VSTHRD113.md 27 | - href: VSTHRD114.md 28 | - href: VSTHRD115.md 29 | - href: VSTHRD200.md 30 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD107.md: -------------------------------------------------------------------------------- 1 | # VSTHRD107 Await Task within using expression 2 | 3 | The C# `using` statement requires that the used expression implement `IDisposable`. 4 | Because `Task` implements `IDisposable`, one may accidentally omit an `await` operator 5 | and `Dispose` of the `Task` instead of the `T` result itself when `T` derives from `IDisposable`. 6 | 7 | ## Examples of patterns that are flagged by this analyzer 8 | 9 | ```csharp 10 | AsyncSemaphore lck; 11 | using (lck.EnterAsync()) 12 | { 13 | // ... 14 | } 15 | ``` 16 | 17 | ## Solution 18 | 19 | Add the `await` operator within the `using` expression. 20 | 21 | ```csharp 22 | AsyncSemaphore lck; 23 | using (await lck.EnterAsync()) 24 | { 25 | // ... 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/EmptyStruct.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft.VisualStudio.Threading; 5 | 6 | /// 7 | /// An empty struct. 8 | /// 9 | /// 10 | /// This can save 4 bytes over System.Object when a type argument is required for a generic type, but entirely unused. 11 | /// 12 | internal readonly struct EmptyStruct 13 | { 14 | /// 15 | /// Gets an instance of the empty struct. 16 | /// 17 | internal static EmptyStruct Instance 18 | { 19 | get { return default(EmptyStruct); } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/NativeAOTCompatibility.Test/NativeAOTCompatibility.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net10.0 5 | false 6 | 7 | 8 | $(TargetFrameworks);net10.0-windows 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/SemaphoreFaultedException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// Exception thrown when a is in a faulted state. 10 | /// 11 | public class SemaphoreFaultedException : InvalidOperationException 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public SemaphoreFaultedException() 17 | : base(Strings.SemaphoreMisused) 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "Microsoft Corporation", 6 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", 7 | "variables": { 8 | "licenseName": "MIT", 9 | "licenseFile": "LICENSE" 10 | }, 11 | "fileNamingConvention": "metadata", 12 | "xmlHeader": false 13 | }, 14 | "orderingRules": { 15 | "usingDirectivesPlacement": "outsideNamespace" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /azure-pipelines/vs-insertion-script.ps1: -------------------------------------------------------------------------------- 1 | # List of build artifact files [Source => Destination] to be committed into the VS repo. 2 | $FilesToCommit = @{ 3 | "$env:PROFILINGINPUTSPROPSNAME" = "src/Tests/config/runsettings/Official/OptProf/External/$env:PROFILINGINPUTSPROPSNAME"; 4 | } 5 | 6 | foreach ($File in $FilesToCommit.GetEnumerator()) { 7 | $SourcePath = Join-Path $PSScriptRoot $File.Key 8 | if (Test-Path $SourcePath) { 9 | $DestinationPath = Join-Path (Get-Location) $File.Value 10 | Write-Host "Copying $SourcePath to $DestinationPath" 11 | Copy-Item -Path $SourcePath -Destination $DestinationPath 12 | git add $DestinationPath 13 | } 14 | else { 15 | Write-Host "$SourcePath is not present, skipping" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /azure-pipelines/PoliCheckExclusions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NODE_MODULES|.STORE 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 4.13.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/IAsyncDisposable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// Defines an asynchronous method to release allocated resources. 10 | /// 11 | /// 12 | /// Consider implementing instead. 13 | /// 14 | public interface IAsyncDisposable 15 | { 16 | /// 17 | /// Performs application-defined tasks associated with freeing, 18 | /// releasing, or resetting unmanaged resources asynchronously. 19 | /// 20 | Task DisposeAsync(); 21 | } 22 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/IHangReportContributor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// Provides a facility to produce reports that may be useful when analyzing hangs. 10 | /// 11 | public interface IHangReportContributor 12 | { 13 | /// 14 | /// Contributes data for a hang report. 15 | /// 16 | /// The hang report contribution. Null values should be ignored. 17 | [RequiresUnreferencedCode(Reasons.DiagnosticAnalysisOnly)] 18 | HangReportContribution GetHangReport(); 19 | } 20 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Reflection; 5 | 6 | public class ValidityTests 7 | { 8 | /// 9 | /// Verifies that the library we're testing is not in the GAC, since if it is, 10 | /// we're almost certainly not testing what was just built. 11 | /// 12 | [Fact] 13 | public void ProductNotInGac() 14 | { 15 | Assert.False( 16 | typeof(AsyncBarrier).GetTypeInfo().Assembly.Location.Contains("GAC"), 17 | $"{typeof(AsyncBarrier).GetTypeInfo().Assembly.GetName().Name} was loaded from the GAC. Run UnGac.cmd."); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | README.md 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PackageIcon.png 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/IllegalSemaphoreUsageException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Globalization; 6 | 7 | namespace Microsoft.VisualStudio.Threading; 8 | 9 | /// 10 | /// Exception which is thrown when the contract of a is violated. 11 | /// 12 | public class IllegalSemaphoreUsageException : InvalidOperationException 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public IllegalSemaphoreUsageException(string message) 18 | : base(message) 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tools/Get-NuGetTool.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads the NuGet.exe tool and returns the path to it. 4 | .PARAMETER NuGetVersion 5 | The version of the NuGet tool to acquire. 6 | #> 7 | Param( 8 | [Parameter()] 9 | [string]$NuGetVersion='6.14.0' 10 | ) 11 | 12 | $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" 13 | $binaryToolsPath = Join-Path $toolsPath $NuGetVersion 14 | if (!(Test-Path $binaryToolsPath)) { $null = mkdir $binaryToolsPath } 15 | $nugetPath = Join-Path $binaryToolsPath nuget.exe 16 | 17 | if (!(Test-Path $nugetPath)) { 18 | Write-Host "Downloading nuget.exe $NuGetVersion..." -ForegroundColor Yellow 19 | (New-Object System.Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/v$NuGetVersion/NuGet.exe", $nugetPath) 20 | } 21 | 22 | return (Resolve-Path $nugetPath).Path 23 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | - 'v*.*' 7 | - 'validate/*' 8 | paths: 9 | exclude: 10 | - doc/ 11 | - '*.md' 12 | - .vscode/ 13 | - .github/ 14 | - azure-pipelines/release.yml 15 | 16 | parameters: 17 | - name: EnableMacOSBuild 18 | displayName: Build on macOS 19 | type: boolean 20 | default: false # macOS is often bogged down in Azure Pipelines 21 | - name: RunTests 22 | displayName: Run tests 23 | type: boolean 24 | default: true 25 | 26 | variables: 27 | - template: /azure-pipelines/BuildStageVariables.yml@self 28 | 29 | jobs: 30 | - template: azure-pipelines/build.yml 31 | parameters: 32 | Is1ESPT: false 33 | EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} 34 | RunTests: ${{ parameters.RunTests }} 35 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.Win32; 5 | 6 | namespace Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher; 7 | 8 | internal static class Program 9 | { 10 | private static void Main() 11 | { 12 | // Watch a registry key. Then try to exit the program. 13 | // If in Win7 support mode we start a thread to monitor for registry changes, 14 | // this verifies that the thread is a *background* thread that won't keep the process running. 15 | RegistryKey? key = Registry.CurrentUser.OpenSubKey("SOFTWARE"); 16 | key.WaitForChangeAsync(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | true 5 | Microsoft.VisualStudio.Threading.Analyzers.Only 6 | false 7 | $(NoWarn);CS1591 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Directory.Build.rsp: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # This file contains command-line options that MSBuild will process as part of 3 | # every build, unless the "/noautoresponse" switch is specified. 4 | # 5 | # MSBuild processes the options in this file first, before processing the 6 | # options on the command line. As a result, options on the command line can 7 | # override the options in this file. However, depending on the options being 8 | # set, the overriding can also result in conflicts. 9 | # 10 | # NOTE: The "/noautoresponse" switch cannot be specified in this file, nor in 11 | # any response file that is referenced by this file. 12 | #------------------------------------------------------------------------------ 13 | /nr:false 14 | /m 15 | /verbosity:minimal 16 | /clp:Summary;ForceNoAlign 17 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/Boxed.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft.VisualStudio.Threading; 5 | 6 | internal static class Boxed 7 | { 8 | /// 9 | /// Returns an object containing . 10 | /// 11 | public static readonly object True = true; 12 | 13 | /// 14 | /// Returns an object containing . 15 | /// 16 | public static readonly object False = false; 17 | 18 | /// 19 | /// Returns an object containing specified value. 20 | /// 21 | public static object Box(bool value) 22 | { 23 | return value ? True : False; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Microsoft.VisualStudio.Threading.Analyzers 5 | true 6 | false 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>microsoft/vs-renovate-presets:microbuild", 5 | "github>microsoft/vs-renovate-presets:vs_main_dependencies", 6 | "github>microsoft/vs-renovate-presets:dotnet_packages_LTS", 7 | "github>microsoft/vs-renovate-presets:xunitv2" 8 | ], 9 | "packageRules": [ 10 | { 11 | "matchJsonata": ["sharedVariableName='CodeAnalysisVersion'"], 12 | "enabled": false 13 | } 14 | ], 15 | "customManagers": [ 16 | { 17 | "customType": "regex", 18 | "datasourceTemplate": "nuget", 19 | "managerFilePatterns": [ 20 | "test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs" 21 | ], 22 | "matchStrings": [ 23 | "PackageIdentity\\(\"(?[^\"]+)\", \"(?[^\"]+)\"\\)" 24 | ] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Microsoft.VisualStudio.Threading.Analyzers 5 | true 6 | false 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "powershell": { 6 | "version": "7.5.4", 7 | "commands": [ 8 | "pwsh" 9 | ], 10 | "rollForward": false 11 | }, 12 | "dotnet-coverage": { 13 | "version": "18.1.0", 14 | "commands": [ 15 | "dotnet-coverage" 16 | ], 17 | "rollForward": false 18 | }, 19 | "nbgv": { 20 | "version": "3.9.50", 21 | "commands": [ 22 | "nbgv" 23 | ], 24 | "rollForward": false 25 | }, 26 | "docfx": { 27 | "version": "2.78.4", 28 | "commands": [ 29 | "docfx" 30 | ], 31 | "rollForward": false 32 | }, 33 | "nerdbank.dotnetrepotools": { 34 | "version": "1.0.92", 35 | "commands": [ 36 | "repo" 37 | ], 38 | "rollForward": false 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | // List of extensions which should be recommended for users of this workspace. 5 | "recommendations": [ 6 | "ms-azure-devops.azure-pipelines", 7 | "ms-dotnettools.csharp", 8 | "k--kato.docomment", 9 | "editorconfig.editorconfig", 10 | "esbenp.prettier-vscode", 11 | "pflannery.vscode-versionlens", 12 | "davidanson.vscode-markdownlint", 13 | "dotjoshjohnson.xml", 14 | "ms-vscode-remote.remote-containers", 15 | "ms-azuretools.vscode-docker", 16 | "tintoy.msbuild-project-tools" 17 | ], 18 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 19 | "unwantedRecommendations": [] 20 | } 21 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | public class GenericParameterHelper 7 | { 8 | public GenericParameterHelper() 9 | { 10 | this.Data = new Random().Next(); 11 | } 12 | 13 | public GenericParameterHelper(int data) 14 | { 15 | this.Data = data; 16 | } 17 | 18 | public int Data { get; set; } 19 | 20 | public override bool Equals(object? obj) 21 | { 22 | if (obj is GenericParameterHelper other) 23 | { 24 | return this.Data == other.Data; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | public override int GetHashCode() 31 | { 32 | return this.Data; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD114.md: -------------------------------------------------------------------------------- 1 | # VSTHRD114 Avoid returning a null Task 2 | 3 | Returning `null` from a non-async `Task`/`Task` method will cause a `NullReferenceException` at runtime. This problem can be avoided by returning `Task.CompletedTask`, `Task.FromResult(null)` or `Task.FromResult(default(T))` instead. 4 | 5 | ## Examples of patterns that are flagged by this analyzer 6 | 7 | Any non-async `Task` returning method with an explicit `return null;` will be flagged. 8 | 9 | ```csharp 10 | Task DoAsync() { 11 | return null; 12 | } 13 | 14 | Task GetSomethingAsync() { 15 | return null; 16 | } 17 | ``` 18 | 19 | ## Solution 20 | 21 | Return a task like `Task.CompletedTask` or `Task.FromResult`. 22 | 23 | ```csharp 24 | Task DoAsync() { 25 | return Task.CompletedTask; 26 | } 27 | 28 | Task GetSomethingAsync() { 29 | return Task.FromResult(null); 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /docfx/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../src/Microsoft.VisualStudio.Threading", 7 | "files": [ 8 | "**/*.csproj" 9 | ] 10 | } 11 | ], 12 | "dest": "api" 13 | } 14 | ], 15 | "build": { 16 | "content": [ 17 | { 18 | "files": [ 19 | "**/*.{md,yml}" 20 | ], 21 | "exclude": [ 22 | "_site/**" 23 | ] 24 | } 25 | ], 26 | "resource": [ 27 | { 28 | "files": [ 29 | "images/**" 30 | ] 31 | } 32 | ], 33 | "xref": [ 34 | "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" 35 | ], 36 | "output": "_site", 37 | "template": [ 38 | "default", 39 | "modern" 40 | ], 41 | "globalMetadata": { 42 | "_appName": "Microsoft.VisualStudio.Threading", 43 | "_appTitle": "Microsoft.VisualStudio.Threading", 44 | "_enableSearch": true, 45 | "pdf": false 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/RoslynDebug.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace System.Diagnostics; 7 | 8 | internal static class RoslynDebug 9 | { 10 | /// 11 | [Conditional("DEBUG")] 12 | internal static void Assert([DoesNotReturnIf(false)] bool b) 13 | #pragma warning disable SA1405 // Debug.Assert should provide message text 14 | => Debug.Assert(b); 15 | #pragma warning restore SA1405 // Debug.Assert should provide message text 16 | 17 | /// 18 | [Conditional("DEBUG")] 19 | internal static void Assert([DoesNotReturnIf(false)] bool b, string message) 20 | => Debug.Assert(b, message); 21 | } 22 | -------------------------------------------------------------------------------- /azure-pipelines/Get-InsertionPRId.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Look up the pull request URL of the insertion PR. 4 | #> 5 | $stagingFolder = $env:BUILD_STAGINGDIRECTORY 6 | if (!$stagingFolder) { 7 | $stagingFolder = $env:SYSTEM_DEFAULTWORKINGDIRECTORY 8 | if (!$stagingFolder) { 9 | Write-Error "This script must be run in an Azure Pipeline." 10 | exit 1 11 | } 12 | } 13 | $markdownFolder = Join-Path $stagingFolder (Join-Path 'MicroBuild' 'Output') 14 | $markdownFile = Join-Path $markdownFolder 'PullRequestUrl.md' 15 | if (!(Test-Path $markdownFile)) { 16 | Write-Error "This script should be run after the MicroBuildInsertVsPayload task." 17 | exit 2 18 | } 19 | 20 | $insertionPRUrl = Get-Content $markdownFile 21 | if (!($insertionPRUrl -match 'https:.+?/pullrequest/(\d+)')) { 22 | Write-Error "Failed to parse pull request URL: $insertionPRUrl" 23 | exit 3 24 | } 25 | 26 | $Matches[1] 27 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD109.md: -------------------------------------------------------------------------------- 1 | # VSTHRD109 Switch instead of assert in async methods 2 | 3 | Methods that are or can be async should switch to the main thread when necessary 4 | rather than throw an exception if invoked from a different thread. 5 | This allows callers to invoke any async method from any thread 6 | without having to concern themselves with the threading requirements of a method that 7 | can support its own threading requirements by switching. 8 | 9 | ## Examples of patterns that are flagged by this analyzer 10 | 11 | ```csharp 12 | async Task FooAsync() { 13 | ThreadHelper.ThrowIfNotOnUIThread(); 14 | DoStuff(); 15 | await DoMoreStuff(); 16 | } 17 | ``` 18 | 19 | ## Solution 20 | 21 | Use `await SwitchToMainThreadAsync()` instead: 22 | 23 | ```csharp 24 | async Task FooAsync() { 25 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 26 | DoStuff(); 27 | await DoMoreStuff(); 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /test/IsolatedTestHost/TestOutputHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Globalization; 5 | using System.Text; 6 | using Xunit; 7 | 8 | namespace IsolatedTestHost; 9 | 10 | internal class TestOutputHelper : ITestOutputHelper 11 | { 12 | private readonly StringBuilder builder = new(); 13 | 14 | public string Output => this.builder.ToString(); 15 | 16 | public void Write(string message) => this.builder.Append(message); 17 | 18 | public void Write(string format, params object[] args) => this.builder.AppendFormat(format, args); 19 | 20 | public void WriteLine(string message) => this.builder.AppendLine(message); 21 | 22 | public void WriteLine(string format, params object[] args) => this.builder.AppendLine(string.Format(CultureInfo.CurrentCulture, format, args)); 23 | } 24 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading.Tasks; 5 | 6 | public class ReentrantSemaphoreNonJTFTests : ReentrantSemaphoreTestBase 7 | { 8 | public ReentrantSemaphoreNonJTFTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | [Theory] 14 | [MemberData(nameof(AllModes))] 15 | public void NoDeadlockOnSyncBlockingOnSemaphore_NoContention(ReentrantSemaphore.ReentrancyMode mode) 16 | { 17 | this.semaphore = this.CreateSemaphore(mode); 18 | this.ExecuteOnDispatcher(delegate 19 | { 20 | this.semaphore.ExecuteAsync(() => Task.CompletedTask).Wait(this.TimeoutToken); 21 | return Task.CompletedTask; 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/IPendingExecutionRequestState.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.ComponentModel; 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace Microsoft.VisualStudio.Threading 8 | { 9 | /// 10 | /// An optional interface implemented by pending request state posted to the underline synchronization context. It allows synchronization context to remove completed requests. 11 | /// 12 | [EditorBrowsable(EditorBrowsableState.Never)] 13 | [Experimental("VSOnly")] 14 | public interface IPendingExecutionRequestState 15 | { 16 | /// 17 | /// Gets a value indicating whether the current request has been completed, and can be skipped. 18 | /// 19 | bool IsCompleted { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/JoinableTaskCreationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// Specifies flags that control optional behavior for the creation and execution of tasks. 10 | /// 11 | [Flags] 12 | [Serializable] 13 | public enum JoinableTaskCreationOptions 14 | { 15 | /// 16 | /// Specifies that the default behavior should be used. 17 | /// 18 | None = 0x0, 19 | 20 | /// 21 | /// Specifies that a task will be a long-running operation. It provides a hint to the 22 | /// that hang report should not be fired, when the main thread task is blocked on it. 23 | /// 24 | LongRunning = 0x01, 25 | } 26 | -------------------------------------------------------------------------------- /tools/Get-LibTemplateBasis.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Returns the name of the well-known branch in the Library.Template repository upon which HEAD is based. 4 | #> 5 | [CmdletBinding(SupportsShouldProcess = $true)] 6 | Param( 7 | [switch]$ErrorIfNotRelated 8 | ) 9 | 10 | # This list should be sorted in order of decreasing specificity. 11 | $branchMarkers = @( 12 | @{ commit = 'fd0a7b25ccf030bbd16880cca6efe009d5b1fffc'; branch = 'microbuild' }; 13 | @{ commit = '05f49ce799c1f9cc696d53eea89699d80f59f833'; branch = 'main' }; 14 | ) 15 | 16 | foreach ($entry in $branchMarkers) { 17 | if (git rev-list HEAD | Select-String -Pattern $entry.commit) { 18 | return $entry.branch 19 | } 20 | } 21 | 22 | if ($ErrorIfNotRelated) { 23 | Write-Error "Library.Template has not been previously merged with this repo. Please review https://github.com/AArnott/Library.Template/tree/main?tab=readme-ov-file#readme for instructions." 24 | exit 1 25 | } 26 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers/LanguageUtils.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.Operations; 7 | 8 | namespace Microsoft.VisualStudio.Threading.Analyzers; 9 | 10 | public abstract class LanguageUtils 11 | { 12 | public abstract Location? GetLocationOfBaseTypeName(INamedTypeSymbol symbol, INamedTypeSymbol baseType, Compilation compilation, CancellationToken cancellationToken); 13 | 14 | public abstract SyntaxNode IsolateMethodName(IInvocationOperation invocation); 15 | 16 | public abstract SyntaxNode IsolateMethodName(IObjectCreationOperation objectCreation); 17 | 18 | public abstract bool MethodReturnsNullableReferenceType(IMethodSymbol method); 19 | 20 | public abstract bool IsAsyncMethod(SyntaxNode syntaxNode); 21 | } 22 | -------------------------------------------------------------------------------- /src/SosThreadingTools/GalleryManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SosThreadingTools 4 | Contains the !dumpasync extension for use with .NET Framework processes 5 | $Version$ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | !dumpasync 16 | Dump all async state machines and logical stacks in a .NET Framework process. 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. 6 | Please search the existing issues before filing new issues to avoid duplicates. 7 | For new issues, file your bug or feature request as a new Issue. 8 | 9 | Note that this repo is primarily used for Visual Studio and related products and support will be focused on those scenarios. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Microsoft support for this software is available only for its use in officially supported products such as Visual Studio. 14 | Support and servicing is limited to the latest released version. 15 | For more information, see [Visual Studio Product Lifecycle and Servicing](https://learn.microsoft.com/visualstudio/productinfo/vs-servicing). 16 | Assisted support is available from a professional support engineer by opening a ticket with the [Microsoft assisted support team](https://support.serviceshub.microsoft.com/supportforbusiness/onboarding). 17 | -------------------------------------------------------------------------------- /tools/publish-CodeCov.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Uploads code coverage to codecov.io 4 | .PARAMETER CodeCovToken 5 | Code coverage token to use 6 | .PARAMETER PathToCodeCoverage 7 | Path to root of code coverage files 8 | .PARAMETER Name 9 | Name to upload with codecoverge 10 | .PARAMETER Flags 11 | Flags to upload with codecoverge 12 | #> 13 | [CmdletBinding()] 14 | Param ( 15 | [Parameter(Mandatory=$true)] 16 | [string]$CodeCovToken, 17 | [Parameter(Mandatory=$true)] 18 | [string]$PathToCodeCoverage, 19 | [string]$Name, 20 | [string]$Flags 21 | ) 22 | 23 | $RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path 24 | 25 | Get-ChildItem -Recurse -LiteralPath $PathToCodeCoverage -Filter "*.cobertura.xml" | % { 26 | $relativeFilePath = Resolve-Path -relative $_.FullName 27 | 28 | Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow 29 | & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name 30 | } 31 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.before.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableLocalization 3 | type: boolean 4 | default: false 5 | - name: ShouldSkipOptimize 6 | type: boolean 7 | default: false 8 | - name: RealSign 9 | type: boolean 10 | 11 | steps: 12 | - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: 13 | # notice@0 requires CG detection to run first, and non-default branches don't inject it automatically. 14 | # default branch injection (main) is happening too late for notice@0 to run successfully. Adding this as a workaround. 15 | - task: ComponentGovernanceComponentDetection@0 16 | displayName: 🔍 Component Detection 17 | 18 | - task: notice@0 19 | displayName: 🛠️ Generate NOTICE file 20 | inputs: 21 | outputfile: $(System.DefaultWorkingDirectory)/obj/NOTICE 22 | outputformat: text 23 | retryCountOnTaskFailure: 10 # fails when the cloud service is overloaded 24 | continueOnError: ${{ not(parameters.RealSign) }} # Tolerate failures when we're not building something that may ship. 25 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.JointPackage/Microsoft.VisualStudio.Threading.JointPackage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.VisualStudio.Threading 5 | false 6 | false 7 | $(NoWarn);NU5128 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD012.md: -------------------------------------------------------------------------------- 1 | # VSTHRD012 Provide `JoinableTaskFactory` where allowed 2 | 3 | When constructing types or calling methods that accept a `JoinableTaskFactory` 4 | or `JoinableTaskContext`, take the opportunity to supply one if your application 5 | has a main thread with a single threaded `SynchronizationContext` such as WPF or WinForms. 6 | 7 | ## Examples of patterns that are flagged by this analyzer 8 | 9 | ```csharp 10 | void F() { 11 | var o = new AsyncLazy(() => Task.FromResult(1)); // analyzer flags this line 12 | } 13 | ``` 14 | 15 | ## Solution 16 | 17 | Call the overload that accepts a `JoinableTaskFactory` or `JoinableTaskContext` instance: 18 | 19 | ```csharp 20 | void F() { 21 | var o = new AsyncLazy(() => Task.FromResult(1), this.JoinableTaskFactory); 22 | } 23 | ``` 24 | 25 | ## Suppression 26 | 27 | You can suppress the diagnostic by explicitly specifying `null` for the argument: 28 | 29 | ```csharp 30 | void F() { 31 | var o = new AsyncLazy(() => Task.FromResult(1), null); 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /Directory.Traversal.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | false 6 | false 7 | true 8 | true 9 | 10 | 11 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/SosThreadingTools/ExtensionContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace CpsDbg; 9 | 10 | internal static class ExtensionContext 11 | { 12 | [UnmanagedCallersOnly(EntryPoint = nameof(DebugExtensionInitialize), CallConvs = new[] { typeof(CallConvStdcall) })] 13 | public static unsafe int DebugExtensionInitialize(uint* pVersion, uint* pFlags) 14 | { 15 | // Set the extension version to 1, which expects exports with this signature: 16 | // void _stdcall function(IDebugClient *client, const char *args) 17 | *pVersion = DEBUG_EXTENSION_VERSION(1, 0); 18 | *pFlags = 0; 19 | 20 | return 0; 21 | } 22 | 23 | private static uint DEBUG_EXTENSION_VERSION(uint major, uint minor) 24 | { 25 | return ((major & 0xffff) << 16) | (minor & 0xffff); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "files.insertFinalNewline": true, 4 | "files.trimFinalNewlines": true, 5 | "azure-pipelines.1ESPipelineTemplatesSchemaFile": true, 6 | "omnisharp.enableEditorConfigSupport": true, 7 | "omnisharp.enableRoslynAnalyzers": true, 8 | "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, 9 | "dotnet.defaultSolution": "Microsoft.VisualStudio.Threading.slnx", 10 | "editor.formatOnSave": true, 11 | "[xml]": { 12 | "editor.wordWrap": "off" 13 | }, 14 | // Treat these files as Azure Pipelines files 15 | "files.associations": { 16 | "**/azure-pipelines/**/*.yml": "azure-pipelines", 17 | "azure-pipelines.yml": "azure-pipelines" 18 | }, 19 | // Use Prettier as the default formatter for Azure Pipelines files. 20 | // Needs to be explicitly configured: https://github.com/Microsoft/azure-pipelines-vscode#document-formatting 21 | "[azure-pipelines]": { 22 | "editor.defaultFormatter": "esbenp.prettier-vscode", 23 | "editor.formatOnSave": false // enable this when they conform 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /tools/Check-DotNetSdk.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether the .NET Core SDK required by this repo is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | ) 8 | 9 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 10 | if (!$dotnet) { 11 | # Nothing is installed. 12 | Write-Output $false 13 | exit 1 14 | } 15 | 16 | # We need to set the current directory so dotnet considers the SDK required by our global.json file. 17 | Push-Location "$PSScriptRoot\.." 18 | try { 19 | dotnet -h 2>&1 | Out-Null 20 | if (($LASTEXITCODE -eq 129) -or # On Linux 21 | ($LASTEXITCODE -eq -2147450751) # On Windows 22 | ) { 23 | # These exit codes indicate no matching SDK exists. 24 | Write-Output $false 25 | exit 2 26 | } 27 | 28 | # The required SDK is already installed! 29 | Write-Output $true 30 | exit 0 31 | } catch { 32 | # I don't know why, but on some build agents (e.g. MicroBuild), an exception is thrown from the `dotnet` invocation when a match is not found. 33 | Write-Output $false 34 | exit 3 35 | } finally { 36 | Pop-Location 37 | } 38 | -------------------------------------------------------------------------------- /azure-pipelines/publish-codecoverage.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableMacOSBuild 3 | type: boolean 4 | - name: EnableLinuxBuild 5 | type: boolean 6 | 7 | steps: 8 | - download: current 9 | artifact: coverageResults-Windows 10 | displayName: 🔻 Download Windows code coverage results 11 | continueOnError: true 12 | - ${{ if parameters.EnableLinuxBuild }}: 13 | - download: current 14 | artifact: coverageResults-Linux 15 | displayName: 🔻 Download Linux code coverage results 16 | continueOnError: true 17 | - ${{ if parameters.EnableMacOSBuild }}: 18 | - download: current 19 | artifact: coverageResults-macOS 20 | displayName: 🔻 Download macOS code coverage results 21 | continueOnError: true 22 | - powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose 23 | displayName: ⚙ Merge coverage 24 | - task: PublishCodeCoverageResults@2 25 | displayName: 📢 Publish code coverage results to Azure DevOps 26 | inputs: 27 | summaryFileLocation: coveragereport/merged.cobertura.xml 28 | failIfCoverageEmpty: true 29 | -------------------------------------------------------------------------------- /src/SosThreadingTools/Commands.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace CpsDbg; 9 | 10 | internal static class Commands 11 | { 12 | [UnmanagedCallersOnly(EntryPoint = "dumpasync", CallConvs = new[] { typeof(CallConvStdcall) })] 13 | public static unsafe void DumpAsync(IntPtr client, byte* args) 14 | { 15 | ExecuteCommand(new DumpAsyncCommand(client, isRunningAsExtension: true), args); 16 | } 17 | 18 | private static unsafe void ExecuteCommand(ICommandHandler command, byte* args) 19 | { 20 | try 21 | { 22 | string? strArgs = Marshal.PtrToStringAnsi((IntPtr)args); 23 | command.Execute(strArgs ?? string.Empty); 24 | } 25 | catch (Exception ex) 26 | { 27 | Console.WriteLine($"Encountered an unhandled exception running '{command}':"); 28 | Console.WriteLine(ex.ToString()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docfx/docs/features.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | Async synchronization primitives, async collections, TPL and dataflow extensions. The JoinableTaskFactory allows synchronously blocking the UI thread for async work. This package is applicable to any .NET application (not just Visual Studio). 4 | 5 | * Async versions of many threading synchronization primitives 6 | * `AsyncAutoResetEvent` 7 | * `AsyncBarrier` 8 | * `AsyncCountdownEvent` 9 | * `AsyncManualResetEvent` 10 | * `AsyncReaderWriterLock` 11 | * `AsyncSemaphore` 12 | * `ReentrantSemaphore` 13 | * Async versions of very common types 14 | * `AsyncEventHandler` 15 | * `AsyncLazy` 16 | * `AsyncLazyInitializer` 17 | * `AsyncLocal` 18 | * `AsyncQueue` 19 | * Await extension methods 20 | * Await on a `TaskScheduler` to switch to it. 21 | Switch to a background thread with `await TaskScheduler.Default;` 22 | * Await on a `Task` with a timeout 23 | * Await on a `Task` with cancellation 24 | * `JoinableTaskFactory` that allows you to schedule asynchronous or synchronous work 25 | that does not deadlock with the UI thread even when the UI thread needs to 26 | synchronously block on the result. 27 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD004.md: -------------------------------------------------------------------------------- 1 | # VSTHRD004 Await SwitchToMainThreadAsync 2 | 3 | Calls to `JoinableTaskFactory.SwitchToMainThreadAsync` must be awaited 4 | or it is a no-op. 5 | 6 | ## Examples of patterns that are flagged by this analyzer 7 | 8 | ```csharp 9 | void MyMethod() 10 | { 11 | joinableTaskFactory.SwitchToMainThreadAsync(); 12 | UIThreadBoundWork(); 13 | } 14 | ``` 15 | 16 | ## Solution 17 | 18 | Add `await` in front of the call to `JoinableTaskFactory.SwitchToMainThreadAsync`. 19 | 20 | This requires an async context. Here, we fix the problem by making the outer method async: 21 | 22 | ```csharp 23 | async Task MyMethodAsync() 24 | { 25 | await joinableTaskFactory.SwitchToMainThreadAsync(); 26 | UIThreadBoundWork(); 27 | } 28 | ``` 29 | 30 | 31 | Alternatively if found in a synchronous method that cannot be made async, 32 | this failure can be fixed by lifting the code into a delegate passed to `JoinableTaskFactory.Run`: 33 | 34 | ```csharp 35 | void MyMethod() 36 | { 37 | joinableTaskFactory.Run(async delegate 38 | { 39 | await joinableTaskFactory.SwitchToMainThreadAsync(); 40 | UIThreadBoundWork(); 41 | }); 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD104.md: -------------------------------------------------------------------------------- 1 | # VSTHRD104 Offer async option 2 | 3 | When a publicly accessible method uses `JoinableTaskFactory.Run`, there should be 4 | another way to access the async behavior without synchronously blocking the thread 5 | so that an async caller can be async throughout. 6 | 7 | This rule encourages this pattern by recognizing when some method *Foo* exists and 8 | calls `JoinableTaskFactory.Run` that there is also a method *FooAsync*. 9 | The recommended pattern then is for *Foo* to call *FooAsync* from the delegate 10 | passed to `JoinableTaskFactory.Run` so that the implementation only need be written once. 11 | 12 | ## Examples of patterns that are flagged by this analyzer 13 | 14 | ```csharp 15 | public void Foo() { 16 | this.joinableTaskFactory.Run(async delegate { 17 | await Task.Yield(); 18 | }); 19 | } 20 | ``` 21 | 22 | ## Solution 23 | 24 | Add a FooAsync method, and (optionally) call it from the Foo method: 25 | 26 | ```csharp 27 | public void Foo() { 28 | this.joinableTaskFactory.Run(async delegate { 29 | await FooAsync(); 30 | }); 31 | } 32 | 33 | public async Task FooAsync() { 34 | await Task.Yield(); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD106.md: -------------------------------------------------------------------------------- 1 | # VSTHRD106 Use `InvokeAsync` to raise async events 2 | 3 | Asynchronous events (those typed as `AsyncEventHandler`) must be raised carefully to ensure 4 | all event handlers are invoked and awaited on. 5 | 6 | Although C# lets you invoke event handlers naturally, it has no awareness of async event handlers 7 | and thus will not let you correctly await on their invocation nor invoke them sequentially. 8 | 9 | ## Examples of patterns that are flagged by this analyzer 10 | 11 | ```csharp 12 | public AsyncEventHandler Clicked; 13 | 14 | async Task OnClicked() { 15 | await Clicked(this, EventArgs.Empty); // only awaits the first event handler. 16 | } 17 | ``` 18 | 19 | ## Solution 20 | 21 | Use the `InvokeAsync` extension method defined in the `TplExtensions` class and await its result. 22 | This will ensure each event handler completes before invoking the next event handler in the list, 23 | similar to the default behavior for raising synchronous events. 24 | 25 | ```csharp 26 | public AsyncEventHandler Clicked; 27 | 28 | async Task OnClicked() { 29 | await Clicked.InvokeAsync(this, EventArgs.Empty); // await for the completion of all handlers. 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft.VisualStudio.Threading 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved.  4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/AsyncEventHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.VisualStudio.Threading; 11 | 12 | /// 13 | /// An asynchronous event handler. 14 | /// 15 | /// The sender of the event. 16 | /// Event arguments. 17 | /// A task whose completion signals handling is finished. 18 | public delegate Task AsyncEventHandler(object? sender, EventArgs args); 19 | 20 | /// 21 | /// An asynchronous event handler. 22 | /// 23 | /// The type of event arguments. 24 | /// The sender of the event. 25 | /// Event arguments. 26 | /// A task whose completion signals handling is finished. 27 | public delegate Task AsyncEventHandler(object? sender, TEventArgs args); 28 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD103.md: -------------------------------------------------------------------------------- 1 | # VSTHRD103 Call async methods when in an async method 2 | 3 | In a method which is already asynchronous, calls to other methods should 4 | be to their async versions, where they exist. 5 | 6 | ## Examples of patterns that are flagged by this analyzer 7 | 8 | ```csharp 9 | Task DoAsync() 10 | { 11 | file.Read(buffer, 0, 10); 12 | } 13 | ``` 14 | 15 | All methods where an Async-suffixed equivalent exists will produce this warning 16 | when called from a `Task`-returning method. 17 | In addition, calling `Task.Wait()`, `Task.Result` or `Task.GetAwaiter().GetResult()` 18 | will produce this warning. 19 | 20 | ## Solution 21 | 22 | Await the async version of the method: 23 | 24 | ```csharp 25 | async Task DoAsync() 26 | { 27 | await file.ReadAsync(buffer, 0, 10); 28 | } 29 | ``` 30 | 31 | ## Configuration 32 | 33 | This analyzer can be configured to exclude specific APIs from generating diagnostics. 34 | Some APIs may have async versions that are less efficient or inappropriate for certain use cases. 35 | 36 | See our [configuration](configuration.md) topic to learn how to exclude specific methods 37 | using the `vs-threading.SyncMethodsToExcludeFromVSTHRD103.txt` file. 38 | ``` 39 | -------------------------------------------------------------------------------- /azure-pipelines/WIFtoPATauth.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: deadPATServiceConnectionId # The GUID of the PAT-based service connection whose access token must be replaced. 3 | type: string 4 | - name: wifServiceConnectionName # The name of the WIF service connection to use to get the access token. 5 | type: string 6 | - name: resource # The scope for which the access token is requested. 7 | type: string 8 | default: 499b84ac-1321-427f-aa17-267ca6975798 # Azure Artifact feeds (any of them) 9 | 10 | steps: 11 | - task: AzureCLI@2 12 | displayName: 🔏 Authenticate with WIF service connection 13 | inputs: 14 | azureSubscription: ${{ parameters.wifServiceConnectionName }} 15 | scriptType: pscore 16 | scriptLocation: inlineScript 17 | inlineScript: | 18 | $accessToken = az account get-access-token --query accessToken --resource '${{ parameters.resource }}' -o tsv 19 | # Set the access token as a secret, so it doesn't get leaked in the logs 20 | Write-Host "##vso[task.setsecret]$accessToken" 21 | # Override the apitoken of the nuget service connection, for the duration of this stage 22 | Write-Host "##vso[task.setendpoint id=${{ parameters.deadPATServiceConnectionId }};field=authParameter;key=apitoken]$accessToken" 23 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD200.md: -------------------------------------------------------------------------------- 1 | # VSTHRD200 Use `Async` suffix for async methods 2 | 3 | The .NET Guidelines for async methods includes that such methods 4 | should have names that include an "Async" suffix. 5 | 6 | Methods that return awaitable types such as `Task` or `ValueTask` 7 | should have an Async suffix. 8 | Methods that do not return awaitable types should not use the Async suffix. 9 | 10 | ## Examples of patterns that are flagged by this analyzer 11 | 12 | This `Task`-returning method should have a name that ends with Async: 13 | 14 | ```csharp 15 | async Task DoSomething() // analyzer flags this line 16 | { 17 | await Task.Yield(); 18 | } 19 | ``` 20 | 21 | This method should not have a name that ends with Async, since it does not return an awaitable type: 22 | 23 | ```csharp 24 | bool DoSomethingElseAsync() // analyzer flags this line 25 | { 26 | return false; 27 | } 28 | ``` 29 | 30 | ## Solution 31 | 32 | Simply rename the method to end in "Async" (or remove the suffix, as appropriate): 33 | 34 | ```csharp 35 | async Task DoSomethingAsync() 36 | { 37 | await Task.Yield(); 38 | } 39 | 40 | bool DoSomethingElse() 41 | { 42 | return false; 43 | } 44 | ``` 45 | 46 | 47 | A code fix exists to automatically rename such methods. 48 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading.Tasks; 5 | 6 | public class TestUtilitiesTests 7 | { 8 | [Fact] 9 | public void RunTest() 10 | { 11 | TestUtilities.Run(async delegate 12 | { 13 | await Task.Yield(); 14 | }); 15 | } 16 | 17 | [Fact] 18 | public async Task YieldAndNotify() 19 | { 20 | var task1Awaiting = new AsyncManualResetEvent(); 21 | var task1Resuming = new AsyncManualResetEvent(); 22 | var task2ReceivedNotification = new TaskCompletionSource(); 23 | await Task.WhenAll( 24 | Task.Run(async delegate 25 | { 26 | await task2ReceivedNotification.Task.GetAwaiter().YieldAndNotify(task1Awaiting, task1Resuming); 27 | }), 28 | Task.Run(async delegate 29 | { 30 | await task1Awaiting.WaitAsync(); 31 | task2ReceivedNotification.SetAsync().Forget(); 32 | await task1Resuming.WaitAsync(); 33 | })); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/AsyncLocal`1.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft.VisualStudio.Threading; 5 | 6 | /// 7 | /// Stores references such that they are available for retrieval 8 | /// in the same call context. 9 | /// 10 | /// The type of value to store. 11 | public partial class AsyncLocal 12 | where T : class 13 | { 14 | /// 15 | /// The framework version specific instance of AsyncLocal to use. 16 | /// 17 | private readonly System.Threading.AsyncLocal asyncLocal; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public AsyncLocal() 23 | { 24 | this.asyncLocal = new System.Threading.AsyncLocal(); 25 | } 26 | 27 | /// 28 | /// Gets or sets the value to associate with the current CallContext. 29 | /// 30 | public T? Value 31 | { 32 | get { return this.asyncLocal.Value; } 33 | set { this.asyncLocal.Value = value; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Copilot instructions for this repository 2 | 3 | ## High level guidance 4 | 5 | * Review the `CONTRIBUTING.md` file for instructions to build and test the software. 6 | * Set the `NBGV_GitEngine` environment variable to `Disabled` before running any `dotnet` or `msbuild` commands. 7 | 8 | ## Software Design 9 | 10 | * Design APIs to be highly testable, and all functionality should be tested. 11 | * Avoid introducing binary breaking changes in public APIs of projects under `src` unless their project files have `IsPackable` set to `false`. 12 | 13 | ## Testing 14 | 15 | * There should generally be one test project (under the `test` directory) per shipping project (under the `src` directory). Test projects are named after the project being tested with a `.Test` suffix. 16 | * Tests should use the Xunit testing framework. 17 | * Some tests are known to be unstable. When running tests, you should skip the unstable ones by running `dotnet test --filter "TestCategory!=FailsInCloudTest"`. 18 | 19 | ## Coding style 20 | 21 | * Honor StyleCop rules and fix any reported build warnings *after* getting tests to pass. 22 | * In C# files, use namespace *statements* instead of namespace *blocks* for all new files. 23 | * Add API doc comments to all new public and internal members. 24 | -------------------------------------------------------------------------------- /docfx/docs/testing_vs.md: -------------------------------------------------------------------------------- 1 | # Testing Visual Studio extensions and packages 2 | 3 | ## Considerations when using `JoinableTaskFactory` in code called from unit tests 4 | 5 | By default, the [`ThreadHelper.JoinableTaskFactory`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.threadhelper.joinabletaskfactory?view=visualstudiosdk-2019#Microsoft_VisualStudio_Shell_ThreadHelper_JoinableTaskFactory) and [`AsyncPackage.JoinableTaskFactory`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.asyncpackage.joinabletaskfactory?view=visualstudiosdk-2019#Microsoft_VisualStudio_Shell_AsyncPackage_JoinableTaskFactory) properties only works for code running in the VS process. 6 | Unit testing your code that relies on these properties may throw exceptions. 7 | 8 | **Important**: Your product code should *never* instantiate its own `JoinableTaskContext`. 9 | Always use the one from `ThreadHelper.JoinableTaskContext`. 10 | 11 | To get `ThreadHelper` (and `AsyncPackage`) to work within a unit test process, you may use the [VS SDK Test Framework](https://aka.ms/vssdktestfx). 12 | This framework includes instructions both for MSTest and Xunit to enable your tests to run code that includes usage of `ThreadHelper`, the global `IServiceProvider`, and other APIs that typically only work in the VS process. 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/LightUps.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// A non-generic class used to store statics that do not vary by generic type argument. 10 | /// 11 | internal static class LightUps 12 | { 13 | /// 14 | /// Gets a value indicating whether we execute Windows 7 code even on later versions of Windows. 15 | /// 16 | internal const bool ForceWindows7Mode = false; 17 | 18 | /// 19 | /// The for Windows 8. 20 | /// 21 | private static readonly Version Windows8Version = new Version(6, 2, 9200); 22 | 23 | /// 24 | /// Gets a value indicating whether the current operating system is Windows 8 or later. 25 | /// 26 | internal static bool IsWindows8OrLater 27 | { 28 | get 29 | { 30 | return !ForceWindows7Mode 31 | && Environment.OSVersion.Platform == PlatformID.Win32NT 32 | && Environment.OSVersion.Version >= Windows8Version; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/artifacts/deployables.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $result = @{ } 8 | 9 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 10 | if (Test-Path $PackagesRoot) { 11 | $result[$PackagesRoot] = (Get-ChildItem $PackagesRoot -Recurse) 12 | } 13 | 14 | $SosThreadingToolsRoot = "$RepoRoot/bin/SosThreadingTools/$BuildConfiguration/net472" 15 | if (Test-Path $SosThreadingToolsRoot) { 16 | $ArchivePath = "$RepoRoot\obj\SosThreadingTools\SosThreadingTools.zip" 17 | $ArchiveLayout = "$RepoRoot\obj\SosThreadingTools\ArchiveLayout" 18 | if (Test-Path $ArchiveLayout) { Remove-Item -Force $ArchiveLayout -Recurse } 19 | New-Item -Path $ArchiveLayout -ItemType Directory | Out-Null 20 | Copy-Item -Force -Path "$SosThreadingToolsRoot" -Recurse -Exclude "*.xml" -Destination $ArchiveLayout 21 | Rename-Item -Path $ArchiveLayout\net472 $ArchiveLayout\SosThreadingTools 22 | Get-ChildItem -Path $ArchiveLayout\symstore -Recurse | Remove-Item 23 | Compress-Archive -Force -Path $ArchiveLayout\SosThreadingTools -DestinationPath $ArchivePath 24 | 25 | $result[(Split-Path $ArchivePath -Parent)] = $ArchivePath 26 | } 27 | 28 | $result 29 | -------------------------------------------------------------------------------- /.github/workflows/docs_validate.yml: -------------------------------------------------------------------------------- 1 | name: 📃 Docfx Validate 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | - microbuild 10 | 11 | jobs: 12 | build: 13 | name: 📚 Doc validation 14 | runs-on: ubuntu-24.04 15 | steps: 16 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 17 | with: 18 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 19 | - name: 🔗 Markup Link Checker (mlc) 20 | uses: becheran/mlc@18a06b3aa2901ca197de59c8b0b1f54fdba6b3fa # v1.0.0 21 | with: 22 | args: --do-not-warn-for-redirect-to https://learn.microsoft.com*,https://dotnet.microsoft.com/*,https://dev.azure.com/*,https://app.codecov.io/*,https://badges.gitter.im/*,https://github.com/*,https://app.gitter.im/* -p docfx -i https://aka.ms/onboardsupport,https://aka.ms/spot,https://msrc.microsoft.com/*,https://www.microsoft.com/msrc*,https://microsoft.com/msrc*,https://www.npmjs.com/package/*,https://get.dot.net/,https://microsoft.sharepoint.com/* 23 | - name: ⚙ Install prerequisites 24 | run: | 25 | ./init.ps1 -UpgradePrerequisites 26 | dotnet --info 27 | shell: pwsh 28 | - name: 📚 Verify docfx build 29 | run: dotnet docfx docfx/docfx.json --warningsAsErrors --disableGitFeatures 30 | -------------------------------------------------------------------------------- /tools/artifacts/coverageResults.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = Resolve-Path "$PSScriptRoot\..\.." 2 | 3 | $coverageFilesUnderRoot = @(Get-ChildItem "$RepoRoot/*.cobertura.xml" -Recurse | Where-Object {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) 4 | 5 | # Under MTP, coverage files are written directly to the artifacts output directory, 6 | # so we need to look there too. 7 | $ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 8 | $directTestLogs = Join-Path $ArtifactStagingFolder test_logs 9 | $coverageFilesUnderArtifacts = if (Test-Path $directTestLogs) { @(Get-ChildItem "$directTestLogs/*.cobertura.xml" -Recurse) } else { @() } 10 | 11 | # Prepare code coverage reports for merging on another machine 12 | Write-Host "Substituting $repoRoot with `"{reporoot}`"" 13 | @($coverageFilesUnderRoot + $coverageFilesUnderArtifacts) |? { $_ }|% { 14 | $content = Get-Content -LiteralPath $_ |% { $_ -Replace [regex]::Escape($repoRoot), "{reporoot}" } 15 | Set-Content -LiteralPath $_ -Value $content -Encoding UTF8 16 | } 17 | 18 | if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } 19 | 20 | @{ 21 | $directTestLogs = $coverageFilesUnderArtifacts; 22 | $RepoRoot = ( 23 | $coverageFilesUnderRoot + 24 | (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft.VisualStudio.Threading 2 | 3 | Async synchronization primitives, async collections, TPL and dataflow extensions. The JoinableTaskFactory allows synchronously blocking the UI thread for async work. This package is applicable to any .NET application (not just Visual Studio). 4 | 5 | [Full documentation](https://microsoft.github.io/vs-threading/docs/getting-started.html). 6 | 7 | ## Features 8 | 9 | * Async versions of many threading synchronization primitives 10 | * `AsyncAutoResetEvent` 11 | * `AsyncBarrier` 12 | * `AsyncCountdownEvent` 13 | * `AsyncManualResetEvent` 14 | * `AsyncReaderWriterLock` 15 | * `AsyncSemaphore` 16 | * `ReentrantSemaphore` 17 | * Async versions of very common types 18 | * `AsyncEventHandler` 19 | * `AsyncLazy` 20 | * `AsyncLazyInitializer` 21 | * `AsyncLocal` 22 | * `AsyncQueue` 23 | * Await extension methods 24 | * Await on a `TaskScheduler` to switch to it. 25 | Switch to a background thread with `await TaskScheduler.Default;` 26 | * Await on a `Task` with a timeout 27 | * Await on a `Task` with cancellation 28 | * `JoinableTaskFactory` that allows you to schedule asynchronous or synchronous work 29 | that does not deadlock with the UI thread even when the UI thread needs to 30 | synchronously block on the result. 31 | -------------------------------------------------------------------------------- /tools/Check-DotNetRuntime.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether a given .NET Core runtime is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | [Parameter()] 8 | [ValidateSet('Microsoft.AspNetCore.App','Microsoft.NETCore.App')] 9 | [string]$Runtime='Microsoft.NETCore.App', 10 | [Parameter(Mandatory=$true)] 11 | [Version]$Version 12 | ) 13 | 14 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 15 | if (!$dotnet) { 16 | # Nothing is installed. 17 | Write-Output $false 18 | exit 1 19 | } 20 | 21 | Function IsVersionMatch { 22 | Param( 23 | [Parameter()] 24 | $actualVersion 25 | ) 26 | return $actualVersion -and 27 | $Version.Major -eq $actualVersion.Major -and 28 | $Version.Minor -eq $actualVersion.Minor -and 29 | (($Version.Build -eq -1) -or ($Version.Build -eq $actualVersion.Build)) -and 30 | (($Version.Revision -eq -1) -or ($Version.Revision -eq $actualVersion.Revision)) 31 | } 32 | 33 | $installedRuntimes = dotnet --list-runtimes |? { $_.Split()[0] -ieq $Runtime } |% { $v = $null; [Version]::tryparse($_.Split()[1], [ref] $v); $v } 34 | $matchingRuntimes = $installedRuntimes |? { IsVersionMatch -actualVersion $_ } 35 | if (!$matchingRuntimes) { 36 | Write-Output $false 37 | exit 1 38 | } 39 | 40 | Write-Output $true 41 | exit 0 42 | -------------------------------------------------------------------------------- /samples/samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net472 5 | false 6 | 7 | 8 | 9 | 10 | 11 | false 12 | Analyzer 13 | 14 | 15 | false 16 | Analyzer 17 | 18 | 19 | false 20 | Analyzer 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/IsolatedTestHost/ExitCode.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace IsolatedTestHost; 5 | 6 | /// 7 | /// The meanings of each exit code that may be returned from this process. 8 | /// 9 | public enum ExitCode 10 | { 11 | /// 12 | /// The test executed and passed. 13 | /// 14 | TestPassed = 0, 15 | 16 | /// 17 | /// The test executed and failed. 18 | /// 19 | TestFailed, 20 | 21 | /// 22 | /// The test threw SkipException. 23 | /// 24 | TestSkipped, 25 | 26 | /// 27 | /// The test assembly could not be found. 28 | /// 29 | AssemblyNotFound, 30 | 31 | /// 32 | /// The test class could not be found. 33 | /// 34 | TestClassNotFound, 35 | 36 | /// 37 | /// The test method could not be found. 38 | /// 39 | TestMethodNotFound, 40 | 41 | /// 42 | /// The test class or test method took parameters that are not supported by this host. 43 | /// 44 | TestNotSupported, 45 | 46 | /// 47 | /// Too few or too many command line arguments passed to this process. 48 | /// 49 | UnexpectedCommandLineArgs, 50 | } 51 | -------------------------------------------------------------------------------- /tools/Prepare-Legacy-Symbols.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [string]$Path 3 | ) 4 | 5 | $ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" 6 | $ArtifactStagingFolder += '/symbols-legacy' 7 | robocopy $Path $ArtifactStagingFolder /mir /njh /njs /ndl /nfl 8 | $WindowsPdbSubDirName = 'symstore' 9 | 10 | Get-ChildItem "$ArtifactStagingFolder\*.pdb" -Recurse |% { 11 | $dllPath = "$($_.Directory)/$($_.BaseName).dll" 12 | $exePath = "$($_.Directory)/$($_.BaseName).exe" 13 | if (Test-Path $dllPath) { 14 | $BinaryImagePath = $dllPath 15 | } elseif (Test-Path $exePath) { 16 | $BinaryImagePath = $exePath 17 | } else { 18 | Write-Warning "`"$_`" found with no matching binary file." 19 | $BinaryImagePath = $null 20 | } 21 | 22 | if ($BinaryImagePath) { 23 | # Convert the PDB to legacy Windows PDBs 24 | Write-Host "Converting PDB for $_" -ForegroundColor DarkGray 25 | $WindowsPdbDir = "$($_.Directory.FullName)\$WindowsPdbSubDirName" 26 | if (!(Test-Path $WindowsPdbDir)) { mkdir $WindowsPdbDir | Out-Null } 27 | $legacyPdbPath = "$WindowsPdbDir\$($_.BaseName).pdb" 28 | & "$PSScriptRoot\Convert-PDB.ps1" -DllPath $BinaryImagePath -PdbPath $_ -OutputPath $legacyPdbPath 29 | if ($LASTEXITCODE -ne 0) { 30 | Write-Warning "PDB conversion of `"$_`" failed." 31 | } 32 | 33 | Move-Item $legacyPdbPath $_ -Force 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: 📚 Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 9 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 10 | concurrency: 11 | group: pages 12 | cancel-in-progress: false 13 | 14 | jobs: 15 | publish-docs: 16 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 17 | permissions: 18 | actions: read 19 | pages: write 20 | id-token: write 21 | contents: read 22 | environment: 23 | name: github-pages 24 | url: ${{ steps.deployment.outputs.page_url }} 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 28 | with: 29 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 30 | - name: ⚙ Install prerequisites 31 | run: ./init.ps1 -UpgradePrerequisites 32 | 33 | - run: dotnet docfx docfx/docfx.json 34 | name: 📚 Generate documentation 35 | 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 38 | with: 39 | path: docfx/_site 40 | 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 44 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD113.md: -------------------------------------------------------------------------------- 1 | # VSTHRD113 Check for `System.IAsyncDisposable` 2 | 3 | The `Microsoft.VisualStudio.Threading.IAsyncDisposable` interface is obsolete now that the 4 | `System.IAsyncDisposable` interface has been defined for .NET Standard 2.0 and .NET Framework 4.6.1 5 | by the [`Microsoft.Bcl.AsyncInterfaces` NuGet package](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces). 6 | 7 | Existing code that tests for the `Microsoft.VisualStudio.Threading.IAsyncDisposable` interface on some object should also check for `System.IAsyncDisposable` and behave similarly in either case. 8 | New code should consider only supporting the new `System.IAsyncDisposable` interface. 9 | 10 | ## Examples of patterns that are flagged by this analyzer 11 | 12 | The following code only checks for the obsolete interface and is flagged by this diagnostic: 13 | 14 | ```cs 15 | using Microsoft.VisualStudio.Threading; 16 | 17 | if (obj is IAsyncDisposable asyncDisposable) 18 | { 19 | await asyncDisposable.DisposeAsync(); 20 | } 21 | ``` 22 | 23 | ## Solution 24 | 25 | Fix this by adding a code branch for the new interface that behaves similarly 26 | within the same containing code block: 27 | 28 | ```cs 29 | if (obj is Microsoft.VisualStudio.Threading.IAsyncDisposable vsThreadingAsyncDisposable) 30 | { 31 | await vsThreadingAsyncDisposable.DisposeAsync(); 32 | } 33 | else if (obj is System.IAsyncDisposable bclAsyncDisposable) 34 | { 35 | await bclAsyncDisposable.DisposeAsync(); 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /tools/variables/InsertJsonValues.ps1: -------------------------------------------------------------------------------- 1 | $vstsDropNames = & "$PSScriptRoot\VstsDropNames.ps1" 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $BasePath = "$PSScriptRoot\..\..\bin\Packages\$BuildConfiguration\Vsix" 8 | 9 | if (Test-Path $BasePath) { 10 | $vsmanFiles = @() 11 | Get-ChildItem $BasePath *.vsman -Recurse -File | % { 12 | $version = (Get-Content $_.FullName | ConvertFrom-Json).info.buildVersion 13 | $fullPath = (Resolve-Path $_.FullName).Path 14 | $basePath = (Resolve-Path $BasePath).Path 15 | # Cannot use RelativePath or GetRelativePath due to Powershell Core v2.0 limitation 16 | if ($fullPath.StartsWith($basePath, [StringComparison]::OrdinalIgnoreCase)) { 17 | # Get the relative paths then make sure the directory separators match URL format. 18 | $rfn = $fullPath.Substring($basePath.Length).TrimStart('\', '/').Replace('\', '/') 19 | } 20 | else { 21 | $rfn = $fullPath # fallback to full path if it doesn't start with base path 22 | } 23 | 24 | $fn = $_.Name 25 | 26 | # The left side is filename followed by the version and the right side is the drop url and the relative filename 27 | $thisVsManFile = "$fn{$version}=https://vsdrop.corp.microsoft.com/file/v1/$vstsDropNames;$rfn" 28 | $vsmanFiles += $thisVsManFile 29 | } 30 | 31 | [string]::join(',', $vsmanFiles) 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vs-threading 2 | 3 | [![Build Status](https://dev.azure.com/azure-public/vside/_apis/build/status/vs-threading)](https://dev.azure.com/azure-public/vside/_build/latest?definitionId=12) 4 | [![Join the chat at https://gitter.im/vs-threading/Lobby](https://badges.gitter.im/vs-threading/Lobby.svg)](https://gitter.im/vs-threading/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | ## Microsoft.VisualStudio.Threading 7 | 8 | [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Threading.svg)](https://www.nuget.org/packages/Microsoft.VisualStudio.Threading) 9 | 10 | Async synchronization primitives, async collections, TPL and dataflow extensions. The JoinableTaskFactory allows synchronously blocking the UI thread for async work. This package is applicable to any .NET application (not just Visual Studio). 11 | 12 | [Getting started](https://microsoft.github.io/vs-threading/docs/getting-started.html). 13 | 14 | [See the full list of features](https://microsoft.github.io/vs-threading/docs/features.html). 15 | 16 | ## Microsoft.VisualStudio.Threading.Analyzers 17 | 18 | [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Threading.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.VisualStudio.Threading.Analyzers) 19 | 20 | Static code analyzer to detect common mistakes or potential issues regarding threading and async coding. 21 | 22 | [Diagnostic analyzer rules](https://microsoft.github.io/vs-threading/analyzers/index.html). 23 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD002.md: -------------------------------------------------------------------------------- 1 | # VSTHRD002 Avoid problematic synchronous waits 2 | 3 | Synchronously waiting on `Task`, `ValueTask`, or awaiters is dangerous and may cause dead locks. 4 | 5 | ## Examples of patterns that are flagged by this analyzer 6 | 7 | ```csharp 8 | void DoSomething() 9 | { 10 | DoSomethingElseAsync().Wait(); 11 | DoSomethingElseAsync().GetAwaiter().GetResult(); 12 | var result = CalculateSomethingAsync().Result; 13 | } 14 | ``` 15 | 16 | ## Solution 17 | 18 | Please consider the following options: 19 | 20 | 1. Switch to asynchronous wait if the caller is already a "async" method. 21 | 1. Change the chain of callers to be "async" methods, and then change this code to be asynchronous await. 22 | 1. Use `JoinableTaskFactory.Run()` to wait on the tasks or awaiters. 23 | 24 | ```csharp 25 | async Task DoSomethingAsync() 26 | { 27 | await DoSomethingElseAsync(); 28 | await DoSomethingElseAsync(); 29 | var result = await CalculateSomethingAsync(); 30 | } 31 | 32 | void DoSomething() 33 | { 34 | joinableTaskFactory.Run(async delegate 35 | { 36 | await DoSomethingElseAsync(); 37 | await DoSomethingElseAsync(); 38 | var result = await CalculateSomethingAsync(); 39 | }); 40 | } 41 | ``` 42 | 43 | Refer to [Asynchronous and multithreaded programming within VS using the JoinableTaskFactory][1] for more information. 44 | 45 | [1]: https://devblogs.microsoft.com/premier-developer/asynchronous-and-multithreaded-programming-within-vs-using-the-joinabletaskfactory/ 46 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/NullableHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading.Analyzers; 7 | 8 | internal static class NullableHelpers 9 | { 10 | /// 11 | /// Converts a delegate which can return to a delegate which does not return 12 | /// . The safety of the conversion is not checked, so callers are required to ensure the 13 | /// conditions are met so the delegate does not produce a result in practice. 14 | /// 15 | /// The type of the first parameter of the method that the delegate encapsulates. 16 | /// The type of the second parameter of the method that the delegate encapsulates. 17 | /// The type of the return value of the method that the delegate encapsulates. 18 | /// The delegate which, according to the signature, can return . 19 | /// A copy of with a signature that does not return . 20 | internal static Func AsNonNullReturnUnchecked(Func func) 21 | where TResult : class 22 | { 23 | return func!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD102.md: -------------------------------------------------------------------------------- 1 | # VSTHRD102 Implement internal logic asynchronously 2 | 3 | Internal or private methods may be invoked by public methods that are asynchronous. 4 | If the internal method has an opportunity to do work asynchronously, it should do so 5 | in order that async public members can truly be async. 6 | 7 | ## Examples of patterns that are flagged by this analyzer 8 | 9 | ```csharp 10 | public void PublicMethod() 11 | { 12 | DoWork(); 13 | } 14 | 15 | public async Task PublicMethodAsync() 16 | { 17 | DoWork(); 18 | await Task.Yield(); 19 | } 20 | 21 | internal void DoWork() 22 | { 23 | joinableTaskFactory.Run(async delegate // Analyzer will flag this line 24 | { 25 | await DoSomethingAsync(); 26 | }); 27 | } 28 | ``` 29 | 30 | Note how `DoWork()` synchronously blocks for both `PublicMethod()` and `PublicMethodAsync()`. 31 | 32 | ## Solution 33 | 34 | Remove the synchronously blocking behavior and make the method async. 35 | 36 | ```csharp 37 | public void PublicMethod() 38 | { 39 | joinableTaskFactory.Run(() => PublicMethodAsync()); 40 | } 41 | 42 | public async Task PublicMethodAsync() 43 | { 44 | await DoWorkAsync(); 45 | await Task.Yield(); 46 | } 47 | 48 | internal async Task DoWorkAsync() 49 | { 50 | await DoSomethingAsync(); 51 | } 52 | ``` 53 | 54 | Note how `DoWorkAsync()` now allows `PublicMethodAsync()` to do its work asynchronously 55 | while `PublicMethod()` continues to synchronously block, giving your external caller the option 56 | as to whether to do work asynchronously or synchronously. 57 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/RegistryChangeNotificationFilters.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using global::Windows.Win32.System.Registry; 6 | 7 | namespace Microsoft.VisualStudio.Threading; 8 | 9 | /// 10 | /// The various types of data within a registry key that generate notifications 11 | /// when changed. 12 | /// 13 | /// 14 | /// This enum matches the Win32 REG_NOTIFY_CHANGE_* constants. 15 | /// 16 | [Flags] 17 | public enum RegistryChangeNotificationFilters 18 | { 19 | /// 20 | /// Notify the caller if a subkey is added or deleted. 21 | /// 22 | Subkey = (int)REG_NOTIFY_FILTER.REG_NOTIFY_CHANGE_NAME, 23 | 24 | /// 25 | /// Notify the caller of changes to the attributes of the key, 26 | /// such as the security descriptor information. 27 | /// 28 | Attributes = (int)REG_NOTIFY_FILTER.REG_NOTIFY_CHANGE_ATTRIBUTES, 29 | 30 | /// 31 | /// Notify the caller of changes to a value of the key. This can 32 | /// include adding or deleting a value, or changing an existing value. 33 | /// 34 | Value = (int)REG_NOTIFY_FILTER.REG_NOTIFY_CHANGE_LAST_SET, 35 | 36 | /// 37 | /// Notify the caller of changes to the security descriptor of the key. 38 | /// 39 | Security = (int)REG_NOTIFY_FILTER.REG_NOTIFY_CHANGE_SECURITY, 40 | } 41 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Install the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Install language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /tools/variables/_define.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates the variables returned by the _all.ps1 script 4 | into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. 5 | 6 | The build or release definition may have set these variables to override 7 | what the build would do. So only set them if they have not already been set. 8 | #> 9 | 10 | [CmdletBinding()] 11 | param ( 12 | ) 13 | 14 | (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { 15 | # Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive. 16 | $keyCaps = $_.Key.ToUpper() 17 | if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { 18 | Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan 19 | } else { 20 | Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow 21 | if ($env:TF_BUILD) { 22 | # Create two variables: the first that can be used by its simple name and accessible only within this job. 23 | Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)" 24 | # and the second that works across jobs and stages but must be fully qualified when referenced. 25 | Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)" 26 | } elseif ($env:GITHUB_ACTIONS) { 27 | Add-Content -LiteralPath $env:GITHUB_ENV -Value "$keyCaps=$($_.Value)" 28 | } 29 | Set-Item -LiteralPath "env:$keyCaps" -Value $_.Value 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/ApiSamples.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.VisualStudio.Threading; 7 | 8 | public class SuppressRelevanceSample 9 | { 10 | private readonly ReentrantSemaphore semaphore = ReentrantSemaphore.Create(1, null, ReentrantSemaphore.ReentrancyMode.NotAllowed); 11 | 12 | #region SuppressRelevance 13 | public async Task DoSomethingAsync() 14 | { 15 | await this.semaphore.ExecuteAsync(async delegate 16 | { 17 | // field access under the semaphore 18 | // ... 19 | await Task.Yield(); // represents some async work 20 | 21 | // Fire and forget code that uses the semaphore, but should *not* 22 | // inherit our own posession of the semaphore. 23 | using (this.semaphore.SuppressRelevance()) 24 | { 25 | this.DoSomethingLaterAsync().Forget(); // Don't await this, or a deadlock will occur. 26 | } 27 | }); 28 | } 29 | 30 | private async Task DoSomethingLaterAsync() 31 | { 32 | // This semaphore use will not be seen as nested because of our caller's wrapping 33 | // the call in SuppressRelevance. 34 | // So instead of throwing, it will block till its caller releases the semaphore. 35 | await this.semaphore.ExecuteAsync(async delegate 36 | { 37 | // Whatever 38 | await Task.Yield(); // represents some async work 39 | }); 40 | } 41 | #endregion 42 | } 43 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD100.md: -------------------------------------------------------------------------------- 1 | # VSTHRD100 Avoid `async void` methods 2 | 3 | Methods with `async void` signatures make it impossible for their caller to track 4 | the entire asynchronous operation and handle exceptions that may be thrown by that method. 5 | If the method throws an exception, it crashes the process. 6 | 7 | ## Examples of patterns that are flagged by this analyzer 8 | 9 | ```csharp 10 | async void DoSomethingAsync() 11 | { 12 | await SomethingElseAsync(); 13 | } 14 | ``` 15 | 16 | ## Solution 17 | 18 | Change the method to return `Task` instead of `void`. 19 | 20 | ```csharp 21 | async Task DoSomethingAsync() 22 | { 23 | await SomethingElseAsync(); 24 | } 25 | ``` 26 | 27 | A code fix is offered that automatically changes the return type of the method. 28 | 29 | ### Event handlers 30 | 31 | For event handlers, avoid `async void` by using `RunAsync`: 32 | ```csharp 33 | obj.Event += (s, e) => joinableTaskFactory.RunAsync(() => OnEventAsync(s, e)); 34 | } 35 | 36 | private async Task OnEventAsync(object sender, EventArgs e) 37 | { 38 | // async code here. 39 | } 40 | ``` 41 | 42 | When using method group syntax as an argument, you can define the method with the required signature, without the `async` modifier, and define an anonymous delegate or lambda within the method, like this: 43 | 44 | ```cs 45 | var menuItem = new MenuCommand(HandleEvent, commandId); 46 | 47 | private void HandleEvent(object sender, EventArgs e) 48 | { 49 | _ = joinableTaskFactory.RunAsync(async () => 50 | { 51 | // async code 52 | }); 53 | } 54 | ``` 55 | 56 | Refer to [Async/Await - Best Practices in Asynchronous Programming](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) for more info. 57 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD115.md: -------------------------------------------------------------------------------- 1 | # VSTHRD115 Avoid creating a JoinableTaskContext with an explicit `null` `SynchronizationContext` 2 | 3 | Constructing a `JoinableTaskContext` with an explicit `null` `SynchronizationContext` is not recommended as a means to construct an instance for use in unit tests or processes without a main thread. 4 | This is because the constructor will automatically use `SynchronizationContext.Current` in lieu of a non-`null` argument. 5 | If `SynchronizationContext.Current` happens to be non-`null`, the constructor may unexpectedly configure the new instance as if a main thread were present. 6 | 7 | ## Examples of patterns that are flagged by this analyzer 8 | 9 | ```csharp 10 | void SetupJTC() { 11 | this.jtc = new JoinableTaskContext(null, null); 12 | } 13 | ``` 14 | 15 | This code *appears* to configure the `JoinableTaskContext` to not be associated with any `SynchronizationContext`. 16 | But in fact it will be associated with the current `SynchronizationContext` if one is present. 17 | 18 | ## Solution 19 | 20 | If you intended to inherit `SynchronizationContext.Current` to initialize with a main thread, 21 | provide that value explicitly as the second argument to suppress the warning: 22 | 23 | ```cs 24 | void SetupJTC() { 25 | this.jtc = new JoinableTaskContext(null, SynchronizationContext.Current); 26 | } 27 | ``` 28 | 29 | If you intended to create a `JoinableTaskContext` for use in a unit test or in a process without a main thread, 30 | call `JoinableTaskContext.CreateNoOpContext()` instead: 31 | 32 | ```cs 33 | void SetupJTC() { 34 | this.jtc = JoinableTaskContext.CreateNoOpContext(); 35 | } 36 | ``` 37 | 38 | Code fixes are offered to update code to either of the above patterns. 39 | -------------------------------------------------------------------------------- /tools/artifacts/VSInsertion.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures everything needed to insert into VS (NuGet packages, insertion metadata, etc.) 2 | 3 | [CmdletBinding()] 4 | Param ( 5 | ) 6 | 7 | if ($IsMacOS -or $IsLinux) { 8 | # We only package up for insertions on Windows agents since they are where optprof can happen. 9 | Write-Verbose "Skipping VSInsertion artifact since we're not on Windows." 10 | return @{} 11 | } 12 | 13 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 14 | $BuildConfiguration = $env:BUILDCONFIGURATION 15 | if (!$BuildConfiguration) { 16 | $BuildConfiguration = 'Debug' 17 | } 18 | 19 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 20 | $NuGetPackages = "$PackagesRoot/NuGet" 21 | $VsixPackages = "$PackagesRoot/Vsix" 22 | $AzurePipelinesPath = "$RepoRoot/azure-pipelines" 23 | if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { 24 | $InsertionOutputs = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY/InsertionOutputs" 25 | } 26 | 27 | if (!(Test-Path $NuGetPackages) -and !(Test-Path $VsixPackages)) { 28 | Write-Warning "Skipping because NuGet and VSIX packages haven't been built yet." 29 | return @{} 30 | } 31 | 32 | $result = @{ 33 | "$AzurePipelinesPath" = (Get-ChildItem "$AzurePipelinesPath/vs-insertion-script.ps1"); 34 | "$NuGetPackages" = (Get-ChildItem $NuGetPackages -Recurse); 35 | } 36 | 37 | if (Test-Path $VsixPackages) { 38 | $result["$PackagesRoot"] += Get-ChildItem $VsixPackages -Recurse 39 | } 40 | 41 | if ($InsertionOutputs -and $env:PROFILINGINPUTSPROPSNAME -and (Test-Path "$InsertionOutputs/$env:PROFILINGINPUTSPROPSNAME")) { 42 | $result[$InsertionOutputs] = (Get-ChildItem "$InsertionOutputs/$env:PROFILINGINPUTSPROPSNAME"); # OptProf ProfilingInputs 43 | } 44 | 45 | $result 46 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Uninstall the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Uninstall language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | try 46 | { 47 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 48 | } 49 | catch 50 | { 51 | 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /azure-pipelines/install-dependencies.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: initArgs 3 | type: string 4 | default: '' 5 | - name: needsAzurePublicFeeds 6 | type: boolean 7 | default: true # If nuget.config pulls from the azure-public account, we need to authenticate when building on the devdiv account. 8 | - name: setVariables 9 | type: boolean 10 | default: true 11 | 12 | steps: 13 | - ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 14 | - template: WIFtoPATauth.yml 15 | parameters: 16 | wifServiceConnectionName: azure-public/vside package pull 17 | deadPATServiceConnectionId: 46f0d4d4-9fff-4c58-a1ab-3b8f97e3b78a # azure-public/msft_consumption_public 18 | 19 | - task: NuGetAuthenticate@1 20 | displayName: 🔏 Authenticate NuGet feeds 21 | inputs: 22 | ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 23 | nuGetServiceConnections: azure-public/msft_consumption_public 24 | 25 | - powershell: | 26 | $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors 27 | .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider 28 | dotnet --info 29 | 30 | # Print mono version if it is present. 31 | if (Get-Command mono -ErrorAction SilentlyContinue) { 32 | mono --version 33 | } 34 | displayName: ⚙ Install prerequisites 35 | 36 | - ${{ if parameters.setVariables }}: 37 | - powershell: tools/variables/_define.ps1 38 | failOnStderr: true 39 | displayName: ⚙ Set pipeline variables based on source 40 | name: SetPipelineVariables 41 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/InternalUtilities.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// Internal helper/extension methods for this assembly's own use. 10 | /// 11 | internal static class InternalUtilities 12 | { 13 | /// 14 | /// Removes an element from the middle of a queue without disrupting the other elements. 15 | /// 16 | /// The element to remove. 17 | /// The queue to modify. 18 | /// The value to remove. 19 | /// 20 | /// If a value appears multiple times in the queue, only its first entry is removed. 21 | /// 22 | internal static bool RemoveMidQueue(this Queue queue, T valueToRemove) 23 | where T : class 24 | { 25 | Requires.NotNull(queue, nameof(queue)); 26 | Requires.NotNull(valueToRemove, nameof(valueToRemove)); 27 | 28 | int originalCount = queue.Count; 29 | int dequeueCounter = 0; 30 | bool found = false; 31 | while (dequeueCounter < originalCount) 32 | { 33 | dequeueCounter++; 34 | T dequeued = queue.Dequeue(); 35 | if (!found && dequeued == valueToRemove) 36 | { // only find 1 match 37 | found = true; 38 | } 39 | else 40 | { 41 | queue.Enqueue(dequeued); 42 | } 43 | } 44 | 45 | return found; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/copilot-setup-steps.yml: -------------------------------------------------------------------------------- 1 | name: 💪🏼 Copilot Setup Steps 2 | 3 | # Automatically run the setup steps when they are changed to allow for easy validation, and 4 | # allow manual testing through the repository's "Actions" tab 5 | on: 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - main 10 | paths: 11 | - .github/workflows/copilot-setup-steps.yml 12 | pull_request: 13 | paths: 14 | - .github/workflows/copilot-setup-steps.yml 15 | 16 | jobs: 17 | # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. 18 | copilot-setup-steps: 19 | runs-on: ubuntu-latest 20 | # Set the permissions to the lowest permissions possible needed for your steps. 21 | # Copilot will be given its own token for its operations. 22 | permissions: 23 | # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. 24 | contents: read 25 | 26 | # You can define any steps you want, and they will run before the agent starts. 27 | # If you do not check out your code, Copilot will do this for you. 28 | steps: 29 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 30 | with: 31 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 32 | - name: ⚙ Install prerequisites 33 | run: | 34 | ./init.ps1 -UpgradePrerequisites -NoNuGetCredProvider 35 | dotnet --info 36 | 37 | # Print mono version if it is present. 38 | if (Get-Command mono -ErrorAction SilentlyContinue) { 39 | mono --version 40 | } 41 | shell: pwsh 42 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD108.md: -------------------------------------------------------------------------------- 1 | # VSTHRD108 Assert thread affinity unconditionally 2 | 3 | When a method has thread affinity and throws if called from the wrong thread, it should do so without regard to any other condition. This helps ensure the caller will notice early during development that they are calling from the wrong thread. Extra conditions can hide the problem till end users discover an application failure. 4 | 5 | ## Examples of patterns that are flagged by this analyzer 6 | 7 | ```csharp 8 | private int? age; 9 | 10 | public int GetAge() 11 | { 12 | if (!this.age.HasValue) 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | this.age = DoExpensiveUIThreadWork(); 16 | } 17 | 18 | return this.age.Value; 19 | } 20 | ``` 21 | 22 | The problem here is that although the UI thread is only strictly required when the field is actually initialized, callers generally cannot predict whether they will be the first or a subsequent caller. If they call from a background thread and tend to be a subsequent caller, no exception will be thrown. But under some conditions in the app when they happen to be the first caller, they'll fail at runtime because they're calling from the background thread. 23 | 24 | ## Solution 25 | 26 | Move the code that throws when not on the UI thread outside the conditional block. 27 | 28 | ```csharp 29 | private int? age; 30 | 31 | public int GetAge() 32 | { 33 | ThreadHelper.ThrowIfNotOnUIThread(); 34 | if (!this.age.HasValue) 35 | { 36 | this.age = DoExpensiveUIThreadWork(); 37 | } 38 | 39 | return this.age.Value; 40 | } 41 | ``` 42 | 43 | ## Configuration 44 | 45 | This analyzer is configurable via the `vs-threading.MainThreadAssertingMethods.txt` file. 46 | See our [configuration](configuration.md) topic for more information. 47 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using CSVerify = Microsoft.VisualStudio.Threading.Analyzers.Tests.CSharpCodeFixVerifier; 5 | 6 | public class VSTHRD100AsyncVoidMethodCodeFixTests 7 | { 8 | [Fact] 9 | public async Task ApplyFixesOnAsyncVoidMethod() 10 | { 11 | var test = @" 12 | using System; 13 | 14 | class Test { 15 | async void F() { 16 | await System.Threading.Tasks.Task.Yield(); 17 | } 18 | } 19 | "; 20 | var withFix = @" 21 | using System; 22 | 23 | class Test { 24 | async System.Threading.Tasks.Task F() { 25 | await System.Threading.Tasks.Task.Yield(); 26 | } 27 | } 28 | "; 29 | DiagnosticResult expected = CSVerify.Diagnostic().WithLocation(5, 16); 30 | await CSVerify.VerifyCodeFixAsync(test, expected, withFix); 31 | } 32 | 33 | [Fact] 34 | public async Task ApplyFixesOnAsyncVoidMethod2() 35 | { 36 | var test = @" 37 | using System; 38 | using System.Threading.Tasks; 39 | 40 | class Test { 41 | async void F() { 42 | await Task.Yield(); 43 | } 44 | } 45 | "; 46 | var withFix = @" 47 | using System; 48 | using System.Threading.Tasks; 49 | 50 | class Test { 51 | async Task F() { 52 | await Task.Yield(); 53 | } 54 | } 55 | "; 56 | DiagnosticResult expected = CSVerify.Diagnostic().WithLocation(6, 16); 57 | await CSVerify.VerifyCodeFixAsync(test, expected, withFix); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | 7 | internal static class AssertEx 8 | { 9 | public static void Equal(T expected, T actual, string message) 10 | { 11 | if (!EqualityComparer.Default.Equals(expected, actual)) 12 | { 13 | throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, message); 14 | } 15 | } 16 | 17 | public static void Equal(T expected, T actual, string formattingMessage, params object[] formattingArgs) 18 | { 19 | if (!EqualityComparer.Default.Equals(expected, actual)) 20 | { 21 | throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, string.Format(CultureInfo.CurrentCulture, formattingMessage, formattingArgs)); 22 | } 23 | } 24 | 25 | public static void NotEqual(T expected, T actual, string message) 26 | { 27 | if (EqualityComparer.Default.Equals(expected, actual)) 28 | { 29 | throw Xunit.Sdk.NotEqualException.ForEqualValues(expected?.ToString() ?? "", actual?.ToString() ?? "", message); 30 | } 31 | } 32 | 33 | public static void NotEqual(T expected, T actual, string formattingMessage, params object[] formattingArgs) 34 | { 35 | if (EqualityComparer.Default.Equals(expected, actual)) 36 | { 37 | throw Xunit.Sdk.NotEqualException.ForEqualValues(expected?.ToString() ?? "", actual?.ToString() ?? "", string.Format(CultureInfo.CurrentCulture, formattingMessage, formattingArgs)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docfx/analyzers/VSTHRD011.md: -------------------------------------------------------------------------------- 1 | # VSTHRD011 Use `AsyncLazy` 2 | 3 | The `Lazy` type executes the value factory just once and 4 | the value factory inherits the context of the first one to request the 5 | `Lazy.Value` property's value. This can lead to deadlocks when 6 | the value factory attempts to switch to the main thread. 7 | 8 | ## Examples of patterns that are flagged by this analyzer 9 | 10 | ### Using `Lazy` where `T` is `Task` 11 | 12 | When `T` is `Task` (because the value factory is an async method), 13 | if the first caller had no access to the main thread, and the value factory 14 | requires it, it will block. If later a second caller calls the `Value` property 15 | and that second caller is blocking the UI thread for its result, it will deadlock. 16 | 17 | ```csharp 18 | var lazy = new Lazy>(async delegate // analyzer flags this line 19 | { 20 | await Task.Yield(); 21 | return 3; 22 | }); 23 | 24 | int value = await lazy.Value; 25 | ``` 26 | 27 | ### Using synchronously blocking methods in `Lazy` value factories 28 | 29 | When the value factory passed to the `Lazy` constructor calls synchronously 30 | blocking methods such as `JoinableTaskFactory.Run`, only the first caller 31 | can help any required transition to the main thread. 32 | 33 | ```csharp 34 | var lazy = new Lazy(delegate 35 | { 36 | return joinableTaskFactory.Run(async delegate { // analyzer flags this line 37 | int result = await SomeAsyncMethod(); 38 | return result + 3; 39 | }); 40 | }); 41 | 42 | int value = lazy.Value; 43 | ``` 44 | 45 | ## Solution 46 | 47 | Use `AsyncLazy` with an async value factory: 48 | 49 | ```csharp 50 | var lazy = new AsyncLazy(async delegate 51 | { 52 | await Task.Yield(); 53 | return 3; 54 | }); 55 | 56 | int value = await lazy.GetValueAsync(); 57 | ``` 58 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/NullableHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | internal static class NullableHelpers 9 | { 10 | /// 11 | /// Converts a delegate which assumes an argument that is never null into a delegate which might be given a null value, 12 | /// without adding an explicit null check. 13 | /// 14 | /// The type of argument to be passed to the delegate. 15 | /// The delegate which, according to the signature, does not expect . 16 | /// The exact same referenced delegate, but with a signature that may expect . 17 | internal static Action AsNullableArgAction(Action action) 18 | where T : class 19 | { 20 | return action!; 21 | } 22 | 23 | /// 24 | /// Converts a delegate which assumes an argument that is never null into a delegate which might be given a null value, 25 | /// without adding an explicit null check. 26 | /// 27 | /// The type of argument to be passed to the delegate. 28 | /// The type of value returned from the delegate. 29 | /// The delegate which, according to the signature, does not expect . 30 | /// The exact same referenced delegate, but with a signature that may expect . 31 | internal static Func AsNullableArgFunc(Func func) 32 | where TArg : class 33 | { 34 | return func!; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/SosThreadingTools/SosThreadingTools.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows 4 | win-x86;win-x64 5 | true 6 | true 7 | true 8 | false 9 | 10 | 11 | false 12 | 13 | $(MSBuildProjectName) 14 | $(MSBuildProjectName)Managed 15 | $(MSBuildProjectName) 16 | $(DnneCompilerUserFlags) /W3 /guard:cf 17 | $(DnneLinkerUserFlags) /guard:cf 18 | true 19 | 20 | A WinDBG extension that contains the !DumpAsync command for .NET Framework processes. 21 | 22 | false 23 | true 24 | $(RepoRootPath)bin\Packages\$(Configuration)\WinDBGGallery\ 25 | 26 | 27 | full 28 | 29 | false 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /azure-pipelines/publish_artifacts.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates all the artifacts described by _all.ps1 4 | into commands that instruct Azure Pipelines to actually collect those artifacts. 5 | #> 6 | 7 | [CmdletBinding()] 8 | param ( 9 | [string]$ArtifactNameSuffix, 10 | [switch]$StageOnly, 11 | [switch]$AvoidSymbolicLinks 12 | ) 13 | 14 | Function Set-PipelineVariable($name, $value) { 15 | if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { 16 | return # already set 17 | } 18 | 19 | #New-Item -LiteralPath "Env:\$name".ToUpper() -Value $value -Force | Out-Null 20 | Write-Host "##vso[task.setvariable variable=$name]$value" 21 | } 22 | 23 | Function Test-ArtifactUploaded($artifactName) { 24 | $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" 25 | Test-Path "env:$varName" 26 | } 27 | 28 | & "$PSScriptRoot/../tools/artifacts/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix -AvoidSymbolicLinks:$AvoidSymbolicLinks |% { 29 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 30 | # will skip this one from a check in the _all.ps1 script. 31 | Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' 32 | Write-Host "Staged artifact $($_.Name) to $($_.Path)" 33 | 34 | if (!$StageOnly) { 35 | if (Test-ArtifactUploaded $_.Name) { 36 | Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray 37 | } else { 38 | Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" 39 | 40 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 41 | # will skip this one from a check in the _all.ps1 script. 42 | Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/JoinableTaskInternals.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.ComponentModel; 6 | 7 | namespace Microsoft.VisualStudio.Threading; 8 | #pragma warning disable RS0016 // Add public types and members to the declared API 9 | #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member 10 | 11 | /// 12 | /// A helper class for integration with Visual Studio. 13 | /// APIs in this file are intended for Microsoft internal use only 14 | /// and are subject to change without notice. 15 | /// 16 | [EditorBrowsable(EditorBrowsableState.Never)] 17 | public static class JoinableTaskInternals 18 | { 19 | public static bool IsMainThreadBlockedByAnyJoinableTask(JoinableTaskContext? joinableTaskContext) 20 | { 21 | return joinableTaskContext?.IsMainThreadBlockedByAnyJoinableTask == true; 22 | } 23 | 24 | public static JoinableTaskToken? GetJoinableTaskToken(JoinableTaskContext? joinableTaskContext) 25 | { 26 | if (joinableTaskContext?.AmbientTask?.WeakSelf is WeakReference currentTask) 27 | { 28 | return new JoinableTaskToken() { JoinableTaskReference = currentTask }; 29 | } 30 | 31 | return null; 32 | } 33 | 34 | public static bool IsMainThreadMaybeBlocked(JoinableTaskToken? joinableTaskToken) 35 | { 36 | if (joinableTaskToken?.JoinableTaskReference?.TryGetTarget(out JoinableTask? joinableTask) == true) 37 | { 38 | if (joinableTask is not null) 39 | { 40 | return joinableTask.MaybeBlockMainThread(); 41 | } 42 | } 43 | 44 | return false; 45 | } 46 | 47 | public struct JoinableTaskToken 48 | { 49 | internal WeakReference? JoinableTaskReference; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | indent_style = space 4 | 5 | # SA1108: Block statements should not contain embedded comments 6 | dotnet_diagnostic.SA1108.severity = none 7 | 8 | # SA1123: Do not place regions within elements 9 | dotnet_diagnostic.SA1123.severity = none 10 | 11 | # SA1124: Do not use regions 12 | dotnet_diagnostic.SA1124.severity = none 13 | 14 | # SA1200: Using directives should be placed correctly 15 | dotnet_diagnostic.SA1200.severity = none 16 | 17 | # SA1201: Elements should appear in the correct order 18 | dotnet_diagnostic.SA1201.severity = silent 19 | 20 | # SA1205: Partial elements should declare access 21 | dotnet_diagnostic.SA1205.severity = none 22 | 23 | # SA1400: Access modifier should be declared 24 | dotnet_diagnostic.SA1400.severity = none 25 | 26 | # SA1402: File may only contains a single type 27 | dotnet_diagnostic.SA1402.severity = none 28 | 29 | # SA1403: File may only contain a single namespace 30 | dotnet_diagnostic.SA1403.severity = none 31 | 32 | # SA1502: Element should not be on a single line 33 | dotnet_diagnostic.SA1502.severity = none 34 | 35 | # SA1515: Single-line comment should be preceded by blank line 36 | dotnet_diagnostic.SA1515.severity = none 37 | 38 | # SA1516: Elements should be separated by blank line 39 | dotnet_diagnostic.SA1516.severity = none 40 | 41 | # SA1600: Elements should be documented 42 | dotnet_diagnostic.SA1600.severity = silent 43 | 44 | # SA1601: Partial elements should be documented 45 | dotnet_diagnostic.SA1601.severity = silent 46 | 47 | # SA1649: File name should match first type name 48 | dotnet_diagnostic.SA1649.severity = none 49 | 50 | # IDE0051: Remove unused private members 51 | dotnet_diagnostic.IDE0051.severity = none 52 | 53 | # CS1591: Missing XML comment for publicly visible type or member 54 | dotnet_diagnostic.CS1591.severity = silent 55 | 56 | # CA1822: Mark members as static 57 | dotnet_diagnostic.CA1822.severity = silent 58 | 59 | # CA1062: Validate arguments of public methods 60 | dotnet_diagnostic.CA1062.severity = silent 61 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/IJoinableTaskDependent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft.VisualStudio.Threading; 5 | 6 | /// 7 | /// Represents a dependent item in the JoinableTask dependency graph, it can be either a or a . 8 | /// 9 | internal interface IJoinableTaskDependent 10 | { 11 | /// 12 | /// Gets the this node belongs to. 13 | /// 14 | JoinableTaskContext JoinableTaskContext { get; } 15 | 16 | /// 17 | /// Gets a value indicating whether we need reference count child dependent node. This is to keep the current behavior of . 18 | /// 19 | bool NeedRefCountChildDependencies { get; } 20 | 21 | /// 22 | /// Get the reference of dependent node to record dependencies. 23 | /// 24 | ref JoinableTaskDependencyGraph.JoinableTaskDependentData GetJoinableTaskDependentData(); 25 | 26 | /// 27 | /// A function is called, when this dependent node is added to be a dependency of a parent node. 28 | /// 29 | void OnAddedToDependency(IJoinableTaskDependent parent); 30 | 31 | /// 32 | /// A function is called, when this dependent node is removed as a dependency of a parent node. 33 | /// 34 | void OnRemovedFromDependency(IJoinableTaskDependent parentNode); 35 | 36 | /// 37 | /// A function is called, when a dependent child is added. 38 | /// 39 | void OnDependencyAdded(IJoinableTaskDependent joinChild); 40 | 41 | /// 42 | /// A function is called, when a dependent child is removed. 43 | /// 44 | void OnDependencyRemoved(IJoinableTaskDependent joinChild); 45 | } 46 | -------------------------------------------------------------------------------- /tools/artifacts/Variables.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures all variables defined in the ..\variables folder. 2 | # It "snaps" the values of these variables where we can compute them during the build, 3 | # and otherwise captures the scripts to run later during an Azure Pipelines environment release. 4 | 5 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") 6 | $ArtifactBasePath = "$RepoRoot/obj/_artifacts" 7 | $VariablesArtifactPath = Join-Path $ArtifactBasePath variables 8 | if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } 9 | 10 | # Copy variables, either by value if the value is calculable now, or by script 11 | Get-ChildItem "$PSScriptRoot/../variables" |% { 12 | $value = $null 13 | if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts 14 | # First check the environment variables in case the variable was set in a queued build 15 | # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, 16 | # and on non-Windows env vars are case sensitive. 17 | $envVarName = $_.BaseName.ToUpper() 18 | if (Test-Path env:$envVarName) { 19 | $value = Get-Content "env:$envVarName" 20 | } 21 | 22 | # If that didn't give us anything, try executing the script right now from its original position 23 | if (-not $value) { 24 | $value = & $_.FullName 25 | } 26 | 27 | if ($value) { 28 | # We got something, so wrap it with quotes so it's treated like a literal value. 29 | $value = "'" + $value.Replace("'", "''") + "'" 30 | } 31 | } 32 | 33 | # If that didn't get us anything, just copy the script itself 34 | if (-not $value) { 35 | $value = Get-Content -LiteralPath $_.FullName 36 | } 37 | 38 | Set-Content -LiteralPath "$VariablesArtifactPath/$($_.Name)" -Value $value 39 | } 40 | 41 | @{ 42 | "$VariablesArtifactPath" = (Get-ChildItem $VariablesArtifactPath -Recurse); 43 | } 44 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Threading/JoinableTaskContextException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.VisualStudio.Threading; 7 | 8 | /// 9 | /// An exception thrown when the configuration provided to the 10 | /// are incorrect or a virtual method is overridden such that it violates a contract. 11 | /// This exception should not be caught. It is thrown when the application has a programming fault. 12 | /// 13 | [Serializable] 14 | public class JoinableTaskContextException : Exception 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public JoinableTaskContextException() 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The message for the exception. 27 | public JoinableTaskContextException(string? message) 28 | : base(message) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The message for the exception. 36 | /// The inner exception. 37 | public JoinableTaskContextException(string? message, Exception? inner) 38 | : base(message, inner) 39 | { 40 | } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | #if NET 46 | [Obsolete] 47 | #endif 48 | protected JoinableTaskContextException( 49 | System.Runtime.Serialization.SerializationInfo info, 50 | System.Runtime.Serialization.StreamingContext context) 51 | : base(info, context) 52 | { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis.CodeFixes; 5 | using Microsoft.CodeAnalysis.CSharp.Testing; 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | 8 | namespace Microsoft.VisualStudio.Threading.Analyzers.Tests; 9 | 10 | public static partial class CSharpCodeFixVerifier 11 | where TAnalyzer : DiagnosticAnalyzer, new() 12 | where TCodeFix : CodeFixProvider, new() 13 | { 14 | public static DiagnosticResult Diagnostic() 15 | => CSharpCodeFixVerifier.Diagnostic(); 16 | 17 | public static DiagnosticResult Diagnostic(string diagnosticId) 18 | => CSharpCodeFixVerifier.Diagnostic(diagnosticId); 19 | 20 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) 21 | => new DiagnosticResult(descriptor); 22 | 23 | public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 24 | { 25 | var test = new Test { TestCode = source }; 26 | test.ExpectedDiagnostics.AddRange(expected); 27 | return test.RunAsync(); 28 | } 29 | 30 | public static Task VerifyCodeFixAsync(string source, string fixedSource) 31 | => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 32 | 33 | public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) 34 | => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); 35 | 36 | public static Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) 37 | { 38 | var test = new Test 39 | { 40 | TestCode = source, 41 | FixedCode = fixedSource, 42 | }; 43 | 44 | test.ExpectedDiagnostics.AddRange(expected); 45 | return test.RunAsync(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.CodeAnalysis.CodeFixes; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 7 | 8 | namespace Microsoft.VisualStudio.Threading.Analyzers.Tests; 9 | 10 | public static partial class VisualBasicCodeFixVerifier 11 | where TAnalyzer : DiagnosticAnalyzer, new() 12 | where TCodeFix : CodeFixProvider, new() 13 | { 14 | public static DiagnosticResult Diagnostic() 15 | => VisualBasicCodeFixVerifier.Diagnostic(); 16 | 17 | public static DiagnosticResult Diagnostic(string diagnosticId) 18 | => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); 19 | 20 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) 21 | => new DiagnosticResult(descriptor); 22 | 23 | public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 24 | { 25 | var test = new Test { TestCode = source }; 26 | test.ExpectedDiagnostics.AddRange(expected); 27 | return test.RunAsync(); 28 | } 29 | 30 | public static Task VerifyCodeFixAsync(string source, string fixedSource) 31 | => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 32 | 33 | public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) 34 | => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); 35 | 36 | public static Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) 37 | { 38 | var test = new Test 39 | { 40 | TestCode = source, 41 | FixedCode = fixedSource, 42 | }; 43 | 44 | test.ExpectedDiagnostics.AddRange(expected); 45 | return test.RunAsync(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /azure-pipelines/PostPRMessage.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(SupportsShouldProcess = $true)] 2 | param( 3 | [Parameter(Mandatory=$true)] 4 | $AccessToken, 5 | [Parameter(Mandatory=$true)] 6 | $Markdown, 7 | [ValidateSet('Active','ByDesign','Closed','Fixed','Pending','Unknown','WontFix')] 8 | $CommentState='Active' 9 | ) 10 | 11 | # See https://learn.microsoft.com/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus 12 | if ($CommentState -eq 'Active') { 13 | $StatusCode = 1 14 | } elseif ($CommentState -eq 'ByDesign') { 15 | $StatusCode = 5 16 | } elseif ($CommentState -eq 'Closed') { 17 | $StatusCode = 4 18 | } elseif ($CommentState -eq 'Fixed') { 19 | $StatusCode = 2 20 | } elseif ($CommentState -eq 'Pending') { 21 | $StatusCode = 6 22 | } elseif ($CommentState -eq 'Unknown') { 23 | $StatusCode = 0 24 | } elseif ($CommentState -eq 'WontFix') { 25 | $StatusCode = 3 26 | } 27 | 28 | # Build the JSON body up 29 | $body = ConvertTo-Json @{ 30 | comments = @(@{ 31 | parentCommentId = 0 32 | content = $Markdown 33 | commentType = 1 34 | }) 35 | status = $StatusCode 36 | } 37 | 38 | Write-Verbose "Posting JSON payload: `n$Body" 39 | 40 | # Post the message to the Pull Request 41 | # https://learn.microsoft.com/rest/api/azure/devops/git/pull-request-threads 42 | $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=5.1" 43 | if ($PSCmdlet.ShouldProcess($url, 'Post comment via REST call')) { 44 | try { 45 | if (!$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { 46 | Write-Error "Posting to the pull request requires that the script is running in an Azure Pipelines context." 47 | exit 1 48 | } 49 | Write-Host "Posting PR comment to: $url" 50 | Invoke-RestMethod -Uri $url -Method POST -Headers @{Authorization = "Bearer $AccessToken"} -Body $Body -ContentType application/json 51 | } 52 | catch { 53 | Write-Error $_ 54 | Write-Error $_.Exception.Message 55 | exit 2 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | public class InternalUtilitiesTests 8 | { 9 | [Fact] 10 | public void RemoveMidQueue_Empty() 11 | { 12 | var queue = new Queue(); 13 | Assert.False(queue.RemoveMidQueue(1)); 14 | } 15 | 16 | [Fact] 17 | public void RemoveMidQueue_OnlyElement() 18 | { 19 | var queue = new Queue(); 20 | var one = new GenericParameterHelper(1); 21 | queue.Enqueue(one); 22 | Assert.False(queue.RemoveMidQueue(new GenericParameterHelper(2))); 23 | Assert.True(queue.RemoveMidQueue(one)); 24 | } 25 | 26 | [Fact] 27 | public void RemoveMidQueue() 28 | { 29 | GenericParameterHelper[]? list = Enumerable.Range(1, 3).Select(i => new GenericParameterHelper(i)).ToArray(); 30 | for (int positionToRemove = 0; positionToRemove < list.Length; positionToRemove++) 31 | { 32 | var queue = new Queue(); 33 | for (int i = 0; i < list.Length; i++) 34 | { 35 | queue.Enqueue(list[i]); 36 | } 37 | 38 | queue.RemoveMidQueue(list[positionToRemove]); 39 | 40 | // Verify that the item we intended to remove is gone. 41 | Assert.False(queue.Contains(list[positionToRemove])); 42 | 43 | // Verify that the remaining elements retained their order. 44 | Assert.Equal(list.Length - 1, queue.Count); 45 | GenericParameterHelper? lastDequeued = null; 46 | do 47 | { 48 | GenericParameterHelper? item = queue.Dequeue(); 49 | if (lastDequeued is object) 50 | { 51 | Assert.True(lastDequeued.Data < item.Data); 52 | } 53 | 54 | lastDequeued = item; 55 | } 56 | while (queue.Count > 0); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tools/Convert-PDB.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Converts between Windows PDB and Portable PDB formats. 4 | .PARAMETER DllPath 5 | The path to the DLL whose PDB is to be converted. 6 | .PARAMETER PdbPath 7 | The path to the PDB to convert. May be omitted if the DLL was compiled on this machine and the PDB is still at its original path. 8 | .PARAMETER OutputPath 9 | The path of the output PDB to write. 10 | #> 11 | [CmdletBinding()] 12 | Param( 13 | [Parameter(Mandatory=$true,Position=0)] 14 | [string]$DllPath, 15 | [Parameter()] 16 | [string]$PdbPath, 17 | [Parameter(Mandatory=$true,Position=1)] 18 | [string]$OutputPath 19 | ) 20 | 21 | if ($IsMacOS -or $IsLinux) { 22 | Write-Error "This script only works on Windows" 23 | return 24 | } 25 | 26 | $version = '1.1.0-beta2-21101-01' 27 | $baseDir = "$PSScriptRoot/../obj/tools" 28 | $pdb2pdbpath = "$baseDir/Microsoft.DiaSymReader.Pdb2Pdb.$version/tools/Pdb2Pdb.exe" 29 | if (-not (Test-Path $pdb2pdbpath)) { 30 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 31 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 32 | Write-Verbose "& (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null" 33 | # This package originally comes from the https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json feed. 34 | # Add this feed as an upstream to whatever feed is in nuget.config if this step fails. 35 | & (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir | Out-Null 36 | if ($LASTEXITCODE -ne 0) { 37 | Write-Error "Failed to install Microsoft.DiaSymReader.Pdb2Pdb. Consider adding https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json as an upstream to your nuget.config feed." 38 | return 39 | } 40 | } 41 | 42 | $args = $DllPath,'/out',$OutputPath,'/nowarn','0021' 43 | if ($PdbPath) { 44 | $args += '/pdb',$PdbPath 45 | } 46 | 47 | Write-Verbose "$pdb2pdbpath $args" 48 | & $pdb2pdbpath $args 49 | --------------------------------------------------------------------------------