diff --git a/.editorconfig b/.editorconfig index 5d7c4f0d..d16dbed0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,48 +1,125 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# top-most EditorConfig file root = true # Don't use tabs for indentation. [*] indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true + +# (Please don't specify an indent_size here; that has too many unintended consequences.) [*.yml] indent_size = 2 indent_style = space -[*.{xml,targets,props}] +# Code files +[*.{cs,csx,vb,vbx,h,cpp,idl}] +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,runsettings}] indent_size = 2 -[*.cs] -indent_style = space -indent_size = 4 +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 -# Make Visual Studio consistent with SA1101 (use "this" prefix), but allow StyleCop to report it -dotnet_style_qualification_for_field = true:silent -dotnet_style_qualification_for_property = true:silent -dotnet_style_qualification_for_method = true:silent -dotnet_style_qualification_for_event = true:silent +# JSON files +[*.json] +indent_size = 2 -# Make Visual Studio consistent with SA1121 (use predefined type), but allow StyleCop to report it -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent -csharp_style_var_for_built_in_types = false:silent - -# Additional settings to make the Visual Studio formatter follow StyleCop +# Dotnet code style settings: +[*.{cs,vb}] +# Sort using and Import directives with System.* appearing first dotnet_sort_system_directives_first = true +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_property = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_event = true:warning -csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = false +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_within_query_expression_clauses = true +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase +dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case + +# Instance fields are camelCase +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# CSharp code style settings: +[*.cs] # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false @@ -50,29 +127,64 @@ csharp_indent_case_contents = true csharp_indent_switch_labels = true csharp_indent_labels = flush_left -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = do_not_ignore -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:warning -# RS2008: Enable analyzer release tracking -dotnet_diagnostic.RS2008.severity = none +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Blocks are allowed +csharp_prefer_braces = true:silent + +[*.cs] + +# SA1130: Use lambda syntax +dotnet_diagnostic.SA1130.severity = silent + +# SA1404: Code analysis suppression should have justification +dotnet_diagnostic.SA1404.severity = suggestion + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1611: Element parameters should be documented +dotnet_diagnostic.SA1611.severity = none + +# SA1615: Element return value should be documented +dotnet_diagnostic.SA1615.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1133: Do not combine attributes +dotnet_diagnostic.SA1133.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = suggestion + +[*.sln] +indent_style = tab diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..5ef3e402 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,50 @@ + + + Debug + $(MSBuildThisFileDirectory) + $(RepoRootPath)obj\$(MSBuildProjectName)\ + $(RepoRootPath)bin\$(MSBuildProjectName)\ + $(RepoRootPath)bin\Packages\$(Configuration)\NuGet\ + 8.0 + enable + true + + Microsoft + Microsoft + © Microsoft. All rights reserved. + MIT + https://github.com/Microsoft/vs-threading + true + true + true + snupkg + + 2.0.61 + + + + + + + + + + + + + + + + + + + + + + + + https://github.com/microsoft/vs-threading/releases/tag/v$(Version) + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..8c119d54 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,2 @@ + + diff --git a/src/Microsoft.VisualStudio.Threading.sln b/Microsoft.VisualStudio.Threading.sln similarity index 81% rename from src/Microsoft.VisualStudio.Threading.sln rename to Microsoft.VisualStudio.Threading.sln index 0528b0dd..6e670456 100644 --- a/src/Microsoft.VisualStudio.Threading.sln +++ b/Microsoft.VisualStudio.Threading.sln @@ -3,37 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28413.118 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers", "Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.csproj", "{536F3F9A-B457-43B8-BC93-CE1C16959037}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers", "src\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.csproj", "{536F3F9A-B457-43B8-BC93-CE1C16959037}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.Tests", "Microsoft.VisualStudio.Threading.Analyzers.Tests\Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj", "{620ED702-B6DA-4454-BF3E-5494D3652724}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.Tests", "test\Microsoft.VisualStudio.Threading.Analyzers.Tests\Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj", "{620ED702-B6DA-4454-BF3E-5494D3652724}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA091F9F-B40A-466F-B09F-22EAD115996F}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - ..\.vsts-ci.yml = ..\.vsts-ci.yml - ..\azure-pipelines\build.yml = ..\azure-pipelines\build.yml + azure-pipelines\build.yml = azure-pipelines\build.yml Directory.Build.props = Directory.Build.props - ..\global.json = ..\global.json - ..\nuget.config = ..\nuget.config + global.json = global.json + nuget.config = nuget.config stylecop.json = stylecop.json version.json = version.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher", "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj", "{4961AA84-088C-46C0-BAC0-F9E87A9F03A7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher", "test\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj", "{4961AA84-088C-46C0-BAC0-F9E87A9F03A7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading", "Microsoft.VisualStudio.Threading\Microsoft.VisualStudio.Threading.csproj", "{D9BB9FB6-3833-44E8-B7A7-DE729FCE214D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading", "src\Microsoft.VisualStudio.Threading\Microsoft.VisualStudio.Threading.csproj", "{D9BB9FB6-3833-44E8-B7A7-DE729FCE214D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests", "Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj", "{CBEDB102-ABAE-40B1-AF3F-A6226DB6713D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests", "test\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj", "{CBEDB102-ABAE-40B1-AF3F-A6226DB6713D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsolatedTestHost", "IsolatedTestHost\IsolatedTestHost.csproj", "{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsolatedTestHost", "test\IsolatedTestHost\IsolatedTestHost.csproj", "{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes", "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj", "{3BDB8F46-A39C-422B-8B0E-89E98B83073F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes", "src\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj", "{3BDB8F46-A39C-422B-8B0E-89E98B83073F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SosThreadingTools", "SosThreadingTools\SosThreadingTools.csproj", "{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SosThreadingTools", "src\SosThreadingTools\SosThreadingTools.csproj", "{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CSharp", "Microsoft.VisualStudio.Threading.Analyzers.CSharp\Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj", "{D5A0D627-7853-43F5-9AF4-E23D062C6ABA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CSharp", "src\Microsoft.VisualStudio.Threading.Analyzers.CSharp\Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj", "{D5A0D627-7853-43F5-9AF4-E23D062C6ABA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic", "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj", "{8CDF7526-D625-4E16-A266-BAF654ABE181}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic", "src\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj", "{8CDF7526-D625-4E16-A266-BAF654ABE181}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/.vsts-ci.yml b/azure-pipelines.yml similarity index 62% rename from .vsts-ci.yml rename to azure-pipelines.yml index 85d0d1bd..b552774b 100644 --- a/.vsts-ci.yml +++ b/azure-pipelines.yml @@ -2,21 +2,23 @@ trigger: branches: include: - master - - 'v16.*' + - microbuild - 'validate/*' paths: - exclude: [".github", "doc", "*.md"] + exclude: + - doc/ + - '*.md' + - .vscode/ + - .github/ variables: TreatWarningsAsErrors: true - UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true BuildConfiguration: Release - BuildPlatform: Any CPU + # codecov_token: 4dc9e7e2-6b01-4932-a180-847b52b43d35 # Get a new one from https://codecov.io/ NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages jobs: - template: azure-pipelines/build.yml parameters: - SignType: ShouldSkipOptimize: diff --git a/azure-pipelines/Convert-PDB.ps1 b/azure-pipelines/Convert-PDB.ps1 index 00549f71..a3212213 100644 --- a/azure-pipelines/Convert-PDB.ps1 +++ b/azure-pipelines/Convert-PDB.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - Builds all projects in this repo. + Converts between Windows PDB and Portable PDB formats. .PARAMETER DllPath The path to the DLL whose PDB is to be converted. .PARAMETER PdbPath diff --git a/azure-pipelines/Darwin.runsettings b/azure-pipelines/Darwin.runsettings new file mode 100644 index 00000000..b444e819 --- /dev/null +++ b/azure-pipelines/Darwin.runsettings @@ -0,0 +1 @@ + diff --git a/azure-pipelines/Get-ProcDump.ps1 b/azure-pipelines/Get-ProcDump.ps1 new file mode 100644 index 00000000..1493fe4b --- /dev/null +++ b/azure-pipelines/Get-ProcDump.ps1 @@ -0,0 +1,14 @@ +<# +.SYNOPSIS +Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed. +#> +$version = '0.0.1' +$baseDir = "$PSScriptRoot\..\obj\tools" +$procDumpToolPath = "$baseDir\procdump.$version\bin" +if (-not (Test-Path $procDumpToolPath)) { + if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } + $baseDir = (Resolve-Path $baseDir).Path # Normalize it + & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null +} + +(Resolve-Path $procDumpToolPath).Path diff --git a/azure-pipelines/Get-TempToolsPath.ps1 b/azure-pipelines/Get-TempToolsPath.ps1 index 97c552c0..bb3da8e3 100644 --- a/azure-pipelines/Get-TempToolsPath.ps1 +++ b/azure-pipelines/Get-TempToolsPath.ps1 @@ -1,7 +1,7 @@ -if ($env:AGENT_TOOLSDIRECTORY) { - $path = "$env:AGENT_TOOLSDIRECTORY\vs-platform\tools" +if ($env:AGENT_TEMPDIRECTORY) { + $path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID" } elseif ($env:localappdata) { - $path = "$env:localappdata\vs-platform\tools" + $path = "$env:localappdata\gitrepos\tools" } else { $path = "$PSScriptRoot\..\obj\tools" } diff --git a/azure-pipelines/Get-nbgv.ps1 b/azure-pipelines/Get-nbgv.ps1 index 925eecdd..a5be2cf7 100644 --- a/azure-pipelines/Get-nbgv.ps1 +++ b/azure-pipelines/Get-nbgv.ps1 @@ -10,11 +10,7 @@ if ($existingTool) { return $existingTool.Path } -if ($env:AGENT_TEMPDIRECTORY) { - $toolInstallDir = "$env:AGENT_TEMPDIRECTORY/$env:BUILD_BUILDID" -} else { - $toolInstallDir = "$PSScriptRoot/../obj/tools" -} +$toolInstallDir = & "$PSScriptRoot/Get-TempToolsPath.ps1" $toolPath = "$toolInstallDir/nbgv" if (!(Test-Path $toolInstallDir)) { New-Item -Path $toolInstallDir -ItemType Directory | Out-Null } diff --git a/azure-pipelines/Linux.runsettings b/azure-pipelines/Linux.runsettings new file mode 100644 index 00000000..b444e819 --- /dev/null +++ b/azure-pipelines/Linux.runsettings @@ -0,0 +1 @@ + diff --git a/azure-pipelines/OptProf.yml b/azure-pipelines/OptProf.yml index 2229935f..b2d88351 100644 --- a/azure-pipelines/OptProf.yml +++ b/azure-pipelines/OptProf.yml @@ -1,20 +1,21 @@ trigger: none pr: none schedules: -- cron: "0 3 * * Tue" # Mon @ 8 or 9 PM Mountain Time (depending on DST) - displayName: Weekly OptProf run - branches: - include: - - 'v16.*' - - master - always: true # we must keep data fresh since optimizationdata drops are purged after 30 days + - cron: "0 3 * * Tue" # Mon @ 8 or 9 PM Mountain Time (depending on DST) + displayName: Weekly OptProf run + branches: + include: + - 'v1*' + - master + always: true # we must keep data fresh since optimizationdata drops are purged after 30 days -resources: - repositories: - - repository: scripts - type: git - name: DeploymentScripts - ref: refs/heads/test +# Avoid errant CI builds: https://developercommunity.visualstudio.com/content/problem/1154409/azure-pipeline-is-triggering-due-to-events-that-ne.html +#resources: +# repositories: +# - repository: scripts +# type: git +# name: DeploymentScripts +# ref: refs/heads/test variables: - name: TreatWarningsAsErrors @@ -31,6 +32,8 @@ variables: value: true - name: PublicRelease value: false # avoid using nice version since we're building a preliminary/unoptimized package +- name: SignType + value: real - group: Library.Template stages: @@ -41,12 +44,11 @@ stages: - template: build.yml parameters: windowsPool: VSEng-MicroBuildVS2019 - SignType: real ShouldSkipOptimize: true - stage: QueueVSBuild jobs: - job: QueueOptProf - pool: VSEng-RMPool + pool: VSEng-ReleasePool variables: InsertPayloadName: vs-threading InsertTopicBranch: team/VS-IDE/vs-threading-OptProf-run-$(Build.BuildId) diff --git a/azure-pipelines/Set-EnvVars.ps1 b/azure-pipelines/Set-EnvVars.ps1 deleted file mode 100644 index 313cecff..00000000 --- a/azure-pipelines/Set-EnvVars.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -<# -.SYNOPSIS - Set environment variables in the environment. - Azure Pipeline and CMD environments are considered. -.PARAMETER Variables - A hashtable of variables to be set. -.OUTPUTS - A boolean indicating whether the environment variables can be expected to propagate to the caller's environment. -#> -[CmdletBinding(SupportsShouldProcess=$true)] -Param( - [Parameter(Mandatory=$true, Position=1)] - $Variables -) - -if ($Variables.Count -eq 0) { - return $true -} - -$cmdInstructions = !$env:TF_BUILD -and $env:PS1UnderCmd -eq '1' -if ($cmdInstructions) { - Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe" - Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue -} - -$Variables.GetEnumerator() |% { - Set-Item -Path env:$($_.Key) -Value $_.Value - - # If we're running in Azure Pipelines, set these environment variables - if ($env:TF_BUILD) { - Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" - } - - if ($cmdInstructions) { - Write-Host "SET $($_.Key)=$($_.Value)" - } -} - -return !$cmdInstructions diff --git a/azure-pipelines/Windows_NT.runsettings b/azure-pipelines/Windows_NT.runsettings new file mode 100644 index 00000000..0b43ecb0 --- /dev/null +++ b/azure-pipelines/Windows_NT.runsettings @@ -0,0 +1,14 @@ + + + + + + + + + %BUILD_ARTIFACTSTAGINGDIRECTORY% + + + + + diff --git a/azure-pipelines/artifacts/Variables.ps1 b/azure-pipelines/artifacts/Variables.ps1 index cfcb7df5..c6330cd3 100644 --- a/azure-pipelines/artifacts/Variables.ps1 +++ b/azure-pipelines/artifacts/Variables.ps1 @@ -2,18 +2,21 @@ # It "snaps" the values of these variables where we can compute them during the build, # and otherwise captures the scripts to run later during an Azure Pipelines environment release. -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -$ArtifactBasePath = "$RepoRoot\obj\_artifacts" -$VariablesArtifactPath = "$ArtifactBasePath\variables" +$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..))) +$ArtifactBasePath = Join-Path $RepoRoot (Join-Path obj _artifacts) +$VariablesArtifactPath = Join-Path $ArtifactBasePath variables if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } # Copy variables, either by value if the value is calculable now, or by script -Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { +Get-ChildItem -Path (Join-Path $PSScriptRoot (Join-Path .. variables)) |% { $value = $null if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts # First check the environment variables in case the variable was set in a queued build - if (Test-Path env:$($_.BaseName)) { - $value = Get-Content "env:$($_.BaseName)" + # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, + # and on non-Windows env vars are case sensitive. + $envVarName = $_.BaseName.ToUpper() + if (Test-Path env:$envVarName) { + $value = Get-Content "env:$envVarName" } # If that didn't give us anything, try executing the script right now from its original position @@ -32,7 +35,7 @@ Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { $value = Get-Content -Path $_.FullName } - Set-Content -Path "$VariablesArtifactPath\$($_.Name)" -Value $value + Set-Content -Path (Join-Path $VariablesArtifactPath $_.Name) -Value $value } @{ diff --git a/azure-pipelines/artifacts/_all.ps1 b/azure-pipelines/artifacts/_all.ps1 old mode 100644 new mode 100755 index 9b030058..efd6bdb3 --- a/azure-pipelines/artifacts/_all.ps1 +++ b/azure-pipelines/artifacts/_all.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + # This script returns all the artifacts that should be collected after a build. # # Each powershell artifact is expressed as an object with these properties: @@ -22,14 +24,13 @@ Function EnsureTrailingSlash($path) { Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse |% { $ArtifactName = $_.BaseName - Write-Host "Collecting files for the $ArtifactName artifact" $fileGroups = & $_ if (!$fileGroups -or $fileGroups.Count -eq 0) { Write-Warning "No files found for the `"$ArtifactName`" artifact." } else { $fileGroups.GetEnumerator() | % { - $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key), [UriKind]::Absolute) + $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) $_.Value | % { if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { $_ = $_.FullName diff --git a/azure-pipelines/artifacts/_pipelines.ps1 b/azure-pipelines/artifacts/_pipelines.ps1 index 4e9ef21a..5bca7c6c 100644 --- a/azure-pipelines/artifacts/_pipelines.ps1 +++ b/azure-pipelines/artifacts/_pipelines.ps1 @@ -5,51 +5,6 @@ param ( [string]$ArtifactNameSuffix ) -$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") -if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { - $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY -} else { - $ArtifactStagingFolder = "$RepoRoot\obj\_artifacts" - if (Test-Path $ArtifactStagingFolder) { - Remove-Item $ArtifactStagingFolder -Recurse -Force - } -} - -function Create-SymbolicLink { - param ( - $Link, - $Target - ) - - if ($Link -eq $Target) { - return - } - - if (Test-Path $Link) { Remove-Item $Link } - $LinkContainer = Split-Path $Link -Parent - if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } - Write-Verbose "Linking $Link to $Target" - if ($IsMacOS -or $IsLinux) { - ln $Target $Link | Out-Null - } else { - cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null - } -} - -# Stage all artifacts -$Artifacts = & "$PSScriptRoot\_all.ps1" -$Artifacts |% { - $DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\') - $Name = "$(Split-Path $_.Source -Leaf)" - - #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow - - if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null } - if (Test-Path -PathType Leaf $_.Source) { # skip folders - Create-SymbolicLink -Link "$DestinationFolder\$Name" -Target $_.Source - } -} - -$Artifacts |% { $_.ArtifactName } | Get-Unique |% { - Write-Host "##vso[artifact.upload containerfolder=$_$ArtifactNameSuffix;artifactname=$_$ArtifactNameSuffix;]$ArtifactStagingFolder/$_$ArtifactNameSuffix" +& "$PSScriptRoot/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix |% { + Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" } diff --git a/azure-pipelines/artifacts/_stage_all.ps1 b/azure-pipelines/artifacts/_stage_all.ps1 new file mode 100644 index 00000000..4e6a6dbe --- /dev/null +++ b/azure-pipelines/artifacts/_stage_all.ps1 @@ -0,0 +1,59 @@ +# This script links all the artifacts described by _all.ps1 +# into a staging directory, reading for uploading to a cloud build artifact store. +# It returns a sequence of objects with Name and Path properties. + +param ( + [string]$ArtifactNameSuffix +) + +$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..))) +if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { + $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY +} else { + $ArtifactStagingFolder = Join-Path $RepoRoot (Join-Path obj _artifacts) + if (Test-Path $ArtifactStagingFolder) { + Remove-Item $ArtifactStagingFolder -Recurse -Force + } +} + +function Create-SymbolicLink { + param ( + $Link, + $Target + ) + + if ($Link -eq $Target) { + return + } + + if (Test-Path $Link) { Remove-Item $Link } + $LinkContainer = Split-Path $Link -Parent + if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } + Write-Verbose "Linking $Link to $Target" + if ($IsMacOS -or $IsLinux) { + ln $Target $Link | Out-Null + } else { + cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null + } +} + +# Stage all artifacts +$Artifacts = & "$PSScriptRoot\_all.ps1" +$Artifacts |% { + $DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\') + $Name = "$(Split-Path $_.Source -Leaf)" + + #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow + + if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null } + if (Test-Path -PathType Leaf $_.Source) { # skip folders + Create-SymbolicLink -Link (Join-Path $DestinationFolder $Name) -Target $_.Source + } +} + +$Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } | Get-Unique |% { + $artifact = New-Object -TypeName PSObject + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_ + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_) + Write-Output $artifact +} diff --git a/azure-pipelines/artifacts/testResults.ps1 b/azure-pipelines/artifacts/testResults.ps1 new file mode 100644 index 00000000..1e4c4c4e --- /dev/null +++ b/azure-pipelines/artifacts/testResults.ps1 @@ -0,0 +1,12 @@ +if ($env:AGENT_TEMPDIRECTORY) { + # The DotNetCoreCLI uses an alternate location to publish these files + $guidRegex = '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$' + @{ + $env:AGENT_TEMPDIRECTORY = (Get-ChildItem $env:AGENT_TEMPDIRECTORY -Directory |? { $_.Name -match $guidRegex } |% { Get-ChildItem "$($_.FullName)\testhost*.dmp","$($_.FullName)\Sequence_*.xml" -Recurse }); + } +} else { + $testRoot = Resolve-Path "$PSScriptRoot\..\..\test" + @{ + $testRoot = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File); + } +} diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml index 1d3b331d..b1a515cb 100644 --- a/azure-pipelines/build.yml +++ b/azure-pipelines/build.yml @@ -2,7 +2,6 @@ parameters: - name: windowsPool type: string default: Hosted Windows 2019 with VS2019 -- name: SignType - name: ShouldSkipOptimize jobs: @@ -13,13 +12,12 @@ jobs: clean: true - template: install-dependencies.yml - - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud -p src' + - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud' displayName: Set build number - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: - template: microbuild.before.yml parameters: - SignType: ${{ parameters.SignType }} ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} - template: dotnet.yml @@ -30,7 +28,7 @@ jobs: - job: Linux condition: ne(variables['OptProf'], 'true') pool: - vmImage: Ubuntu 16.04 + vmImage: Ubuntu 18.04 steps: - checkout: self clean: true @@ -53,8 +51,8 @@ jobs: - Linux - macOS pool: - vmImage: Ubuntu 16.04 - condition: ne(variables['OptProf'], 'true') + vmImage: Ubuntu 18.04 + condition: succeededOrFailed() steps: - checkout: self clean: true diff --git a/azure-pipelines/dotnet.yml b/azure-pipelines/dotnet.yml index a0b12e54..94bfa045 100644 --- a/azure-pipelines/dotnet.yml +++ b/azure-pipelines/dotnet.yml @@ -1,44 +1,41 @@ steps: - # We use VSBuild instead of "dotnet build" on Windows where MicroBuild tasks have to run (since they don't support MSBuild Core yet). - task: VSBuild@1 + displayName: Build Visual Studio solution inputs: msbuildArgs: /t:build,pack /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog" platform: Any CPU configuration: $(BuildConfiguration) - displayName: Build Visual Studio solution condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - script: dotnet build /t:build,pack --no-restore -c $(BuildConfiguration) /p:Platform=NonWindows /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" - displayName: dotnet build and pack - workingDirectory: src + displayName: dotnet build & pack condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) - task: DotNetCoreCLI@2 displayName: dotnet test -f net472 inputs: command: test - arguments: --no-build -c $(BuildConfiguration) -f net472 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true + arguments: --no-build -c $(BuildConfiguration) -f net472 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" testRunTitle: net472-$(Agent.JobName) - workingDirectory: src condition: and(ne(variables['OptProf'], 'true'), eq(variables['Agent.OS'], 'Windows_NT')) - task: DotNetCoreCLI@2 displayName: dotnet test -f netcoreapp2.1 inputs: command: test - arguments: --no-build -c $(BuildConfiguration) -f netcoreapp2.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true + arguments: --no-build -c $(BuildConfiguration) -f netcoreapp2.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" testRunTitle: netcoreapp2.1-$(Agent.JobName) - workingDirectory: src/Microsoft.VisualStudio.Threading.Tests + workingDirectory: test/Microsoft.VisualStudio.Threading.Tests condition: ne(variables['OptProf'], 'true') - task: DotNetCoreCLI@2 displayName: dotnet test -f netcoreapp3.1 inputs: command: test - arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true + arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" testRunTitle: netcoreapp3.1-$(Agent.JobName) - workingDirectory: src/Microsoft.VisualStudio.Threading.Tests + workingDirectory: test/Microsoft.VisualStudio.Threading.Tests condition: ne(variables['OptProf'], 'true') # We have to artifically run this script so that the extra .nupkg is produced for variables/InsertConfigValues.ps1 to notice. @@ -46,23 +43,19 @@ steps: displayName: Prepare VSInsertion artifact condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) -- task: PowerShell@2 - inputs: - filePath: azure-pipelines/variables/_pipelines.ps1 - failOnStderr: true +- powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true displayName: Update pipeline variables based on build outputs condition: succeededOrFailed() -- task: PowerShell@2 - inputs: - filePath: azure-pipelines/artifacts/_pipelines.ps1 - arguments: -ArtifactNameSuffix "-$(Agent.JobName)" +- powershell: azure-pipelines/artifacts/_pipelines.ps1 -ArtifactNameSuffix "-$(Agent.JobName)" + failOnStderr: true displayName: Publish artifacts condition: succeededOrFailed() - task: PublishSymbols@2 inputs: - SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols-Windows + SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols-$(Agent.JobName) SearchPattern: '**/*.pdb' IndexSources: false SymbolServerType: TeamServices diff --git a/azure-pipelines/install-dependencies.yml b/azure-pipelines/install-dependencies.yml index bef82f10..1883c125 100644 --- a/azure-pipelines/install-dependencies.yml +++ b/azure-pipelines/install-dependencies.yml @@ -4,7 +4,8 @@ parameters: steps: - powershell: | - .\init.ps1 -AccessToken '$(System.AccessToken)' ${{ parameters['initArgs'] }} + $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors + .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites dotnet --info displayName: Install prerequisites @@ -14,8 +15,7 @@ steps: inputs: versionSpec: 5.1.x -- task: PowerShell@2 - inputs: - filePath: azure-pipelines/variables/_pipelines.ps1 - failOnStderr: true +- powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true displayName: Set pipeline variables based on source + name: SetPipelineVariables diff --git a/azure-pipelines/microbuild.after.yml b/azure-pipelines/microbuild.after.yml index badd0bbc..e44ad21b 100644 --- a/azure-pipelines/microbuild.after.yml +++ b/azure-pipelines/microbuild.after.yml @@ -1,4 +1,10 @@ steps: +- task: ms-vseng.MicroBuildShipTasks.7c429315-71ba-4cb3-94bb-f829c95f7915.MicroBuildCodesignVerify@2 + displayName: Verify Signed Files + inputs: + TargetFolders: | + $(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration)/NuGet + - task: MicroBuildCleanup@1 condition: succeededOrFailed() displayName: MicroBuild Cleanup @@ -36,5 +42,5 @@ steps: /repoName:$(Build.Repository.Name) /additionalCodexArguments:-bld /additionalCodexArguments:$(Build.ArtifactStagingDirectory)/build_logs - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'), ne(variables['Build.Reason'], 'PullRequest')) continueOnError: true diff --git a/azure-pipelines/microbuild.before.yml b/azure-pipelines/microbuild.before.yml index 8e30f75c..19e937a5 100644 --- a/azure-pipelines/microbuild.before.yml +++ b/azure-pipelines/microbuild.before.yml @@ -1,5 +1,4 @@ parameters: -- name: SignType - name: ShouldSkipOptimize steps: @@ -13,6 +12,6 @@ steps: - task: MicroBuildSigningPlugin@2 inputs: - signType: ${{ parameters.SignType }} + signType: $(SignType) zipSources: false displayName: Install MicroBuild Signing Plugin diff --git a/azure-pipelines/official.yml b/azure-pipelines/official.yml index c8640163..949addaf 100644 --- a/azure-pipelines/official.yml +++ b/azure-pipelines/official.yml @@ -5,10 +5,21 @@ trigger: - 'v16.*' - 'validate/*' paths: - exclude: [".github", "doc", "*.md", ".appveyor.yml", ".travis.yml"] + exclude: + - .github/ + - doc/ + - '*.md' + - .vscode/ +schedules: +- cron: "0 3 * * *" # Daily @ 8 PM PST + displayName: Daily vs-insertion + branches: + include: + - master + - 'v16.*' parameters: -- name: SignType +- name: SignTypeSelection displayName: Sign type type: string default: Test @@ -18,18 +29,75 @@ parameters: type: boolean default: false -variables: - TreatWarningsAsErrors: true - UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - BuildConfiguration: Release - BuildPlatform: Any CPU - NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages - SignType: ${{ parameters.SignType }} +stages: -jobs: -- template: build.yml - parameters: - windowsPool: VSEng-MicroBuildVS2019 - SignType: ${{ parameters.SignType }} - ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} +- stage: Build + variables: + TreatWarningsAsErrors: true + UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BuildConfiguration: Release + BuildPlatform: Any CPU + NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages + SignTypeSelection: ${{ parameters.SignTypeSelection }} + jobs: + - template: build.yml + parameters: + windowsPool: VSEng-MicroBuildVS2019 + ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + +- stage: symbol_archive + displayName: Symbol archival + condition: and(succeeded(), eq(dependencies.Build.outputs['Windows.SetPipelineVariables.SignType'], 'Real')) + jobs: + - job: archive + pool: VSEng-ReleasePool + steps: + - download: current + artifact: Variables-Windows + displayName: Download Variables-Windows artifact + - task: PowerShell@2 + displayName: Set VSTS variables based on artifacts + inputs: + targetType: filePath + filePath: $(Pipeline.Workspace)/Variables-Windows/_pipelines.ps1 + - download: current + artifact: symbols-Windows + displayName: Download symbols-Windows artifact + - task: MicroBuildArchiveSymbols@1 + displayName: Archive $(SymbolsFeatureName) on Symweb + inputs: + SymbolsFeatureName: $(SymbolsFeatureName) + SymbolsSymwebProject: VS + SymbolsUncPath: \\cpvsbuild\drops\$(TeamName)\$(Build.DefinitionName)\$(Build.SourceBranchName)\$(Build.BuildNumber)\Symbols.Archival + SymbolsEmailContacts: vsidemicrobuild + SymbolsAgentPath: $(Pipeline.Workspace)/symbols-Windows + - task: MicroBuildCleanup@1 + displayName: Send Telemetry + +- stage: azure_public_vssdk_feed + displayName: azure-public/vssdk feed + condition: and(succeeded(), eq(dependencies.Build.outputs['Windows.SetPipelineVariables.SignType'], 'Real')) + jobs: + - deployment: push + pool: + vmImage: ubuntu-latest + environment: No-Approval + strategy: + runOnce: + deploy: + steps: + - download: current + artifact: deployables-Windows + displayName: Download deployables-Windows artifact + - task: NuGetToolInstaller@1 + displayName: Use NuGet 5.x + inputs: + versionSpec: 5.x + - task: NuGetCommand@2 + displayName: NuGet push + inputs: + command: push + packagesToPush: $(Pipeline.Workspace)/deployables-Windows/NuGet/*.nupkg + nuGetFeedType: external + publishFeedCredentials: azure-public/vssdk diff --git a/azure-pipelines/publish-deployables.yml b/azure-pipelines/publish-deployables.yml index 481762d4..9cb41fc6 100644 --- a/azure-pipelines/publish-deployables.yml +++ b/azure-pipelines/publish-deployables.yml @@ -3,6 +3,11 @@ steps: displayName: Download deployables artifact: deployables-Windows +- task: NuGetToolInstaller@1 + displayName: Use NuGet 5.x + inputs: + versionSpec: 5.x + - task: NuGetCommand@2 displayName: Push packages to CI feed inputs: diff --git a/azure-pipelines/release-deployment-prep.yml b/azure-pipelines/release-deployment-prep.yml new file mode 100644 index 00000000..6dee28e5 --- /dev/null +++ b/azure-pipelines/release-deployment-prep.yml @@ -0,0 +1,9 @@ +steps: +- download: CI + artifact: Variables-Windows + displayName: Download Variables-Windows artifact +- task: PowerShell@2 + displayName: Set VSTS variables based on artifacts + inputs: + targetType: filePath + filePath: $(Pipeline.Workspace)/CI/Variables-Windows/_pipelines.ps1 diff --git a/azure-pipelines/release.yml b/azure-pipelines/release.yml new file mode 100644 index 00000000..3058512c --- /dev/null +++ b/azure-pipelines/release.yml @@ -0,0 +1,72 @@ +trigger: none # We only want to trigger manually or based on resources +pr: none + +resources: + pipelines: + - pipeline: CI + source: Library # TODO: This should match the name of your CI pipeline + trigger: + tags: + - auto-release + +stages: +- stage: GitHubRelease + displayName: GitHub Release + jobs: + - deployment: create + pool: + vmImage: ubuntu-latest + environment: No-Approval + strategy: + runOnce: + deploy: + steps: + - download: none + - powershell: | + Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" + displayName: Set pipeline name + - task: GitHubRelease@1 + displayName: GitHub release (create) + inputs: + gitHubConnection: AArnott + repositoryName: $(Build.Repository.Name) + target: $(resources.pipeline.CI.sourceCommit) + tagSource: userSpecifiedTag + tag: v$(resources.pipeline.CI.runName) + title: v$(resources.pipeline.CI.runName) + isDraft: true # After running this step, visit the new draft release, edit, and publish. + changeLogCompareToRelease: lastNonDraftRelease + changeLogType: issueBased + changeLogLabels: | + [ + { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, + { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } + ] + +- stage: nuget_org + displayName: nuget.org + dependsOn: GitHubRelease + jobs: + - deployment: push + pool: + vmImage: ubuntu-latest + environment: No-Approval + strategy: + runOnce: + deploy: + steps: + - download: CI + artifact: deployables-Windows + displayName: Download deployables-Windows artifact + patterns: 'deployables-Windows/*' + - task: NuGetToolInstaller@1 + displayName: Use NuGet 5.x + inputs: + versionSpec: 5.x + - task: NuGetCommand@2 + displayName: NuGet push + inputs: + command: push + packagesToPush: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg + nuGetFeedType: external + publishFeedCredentials: VisualStudioExtensibility (nuget.org) diff --git a/azure-pipelines/schedule-only-steps.yml b/azure-pipelines/schedule-only-steps.yml new file mode 100644 index 00000000..ad07a341 --- /dev/null +++ b/azure-pipelines/schedule-only-steps.yml @@ -0,0 +1,3 @@ +steps: +- powershell: echo "##vso[build.addbuildtag]auto-insertion" + displayName: Tag for auto-insertion diff --git a/azure-pipelines/variables/InsertConfigValues.ps1 b/azure-pipelines/variables/InsertConfigValues.ps1 index c3e86989..72c8697f 100644 --- a/azure-pipelines/variables/InsertConfigValues.ps1 +++ b/azure-pipelines/variables/InsertConfigValues.ps1 @@ -1,4 +1,4 @@ -$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin\Packages\$env:BuildConfiguration") +$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin\Packages\$env:BUILDCONFIGURATION") $dirsToSearch = "$BinPath\NuGet\*.nupkg","$BinPath\CoreXT\*.nupkg" |? { Test-Path $_ } $icv=@() diff --git a/azure-pipelines/variables/InsertReviewers.ps1 b/azure-pipelines/variables/InsertReviewers.ps1 new file mode 100644 index 00000000..67ec2d89 --- /dev/null +++ b/azure-pipelines/variables/InsertReviewers.ps1 @@ -0,0 +1 @@ +'Andrew Arnott' diff --git a/azure-pipelines/variables/ShouldSkipOptimize.ps1 b/azure-pipelines/variables/ShouldSkipOptimize.ps1 new file mode 100644 index 00000000..65b670db --- /dev/null +++ b/azure-pipelines/variables/ShouldSkipOptimize.ps1 @@ -0,0 +1 @@ +'false' diff --git a/azure-pipelines/variables/SignType.ps1 b/azure-pipelines/variables/SignType.ps1 new file mode 100644 index 00000000..663d8d49 --- /dev/null +++ b/azure-pipelines/variables/SignType.ps1 @@ -0,0 +1,12 @@ +if ($env:SYSTEM_COLLECTIONID -eq '011b8bdf-6d56-4f87-be0d-0092136884d9') { + if ($env:BUILD_REASON -eq 'Schedule') { + 'real' + } else { + if ($env:SignTypeSelection) { + $env:SignTypeSelection + } else { + 'test' + } + } + } + \ No newline at end of file diff --git a/azure-pipelines/variables/TeamEmail.ps1 b/azure-pipelines/variables/TeamEmail.ps1 new file mode 100644 index 00000000..7cf66982 --- /dev/null +++ b/azure-pipelines/variables/TeamEmail.ps1 @@ -0,0 +1 @@ +'vsidemicrobuild@microsoft.com' diff --git a/azure-pipelines/variables/_all.ps1 b/azure-pipelines/variables/_all.ps1 old mode 100644 new mode 100755 index ed099792..0407d307 --- a/azure-pipelines/variables/_all.ps1 +++ b/azure-pipelines/variables/_all.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + # This script returns a hashtable of build variables that should be set # at the start of a build or release definition's execution. diff --git a/azure-pipelines/variables/_pipelines.ps1 b/azure-pipelines/variables/_pipelines.ps1 index 9fa0c16b..55299550 100644 --- a/azure-pipelines/variables/_pipelines.ps1 +++ b/azure-pipelines/variables/_pipelines.ps1 @@ -5,11 +5,20 @@ # what the build would do. So only set them if they have not already been set. (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { - if (Test-Path -Path "env:$($_.Key.ToUpper())") { - Write-Host "Skipping setting $($_.Key) because variable is already set." -ForegroundColor Cyan + # 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. + $keyCaps = $_.Key.ToUpper() + if (Test-Path -Path "env:$keyCaps") { + Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan } else { - Write-Host "$($_.Key)=$($_.Value)" -ForegroundColor Yellow - Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" - Set-Item -Path "env:$($_.Key)" -Value $_.Value + Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow + if ($env:TF_BUILD) { + # Create two variables: the first that can be used by its simple name and accessible only within this job. + Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)" + # and the second that works across jobs and stages but must be fully qualified when referenced. + Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)" + } elseif ($env:GITHUB_ACTIONS) { + Write-Host "::set-env name=$keyCaps::$($_.Value)" + } + Set-Item -Path "env:$keyCaps" -Value $_.Value } } diff --git a/azure-pipelines/vs-insertion.yml b/azure-pipelines/vs-insertion.yml new file mode 100644 index 00000000..9b23418d --- /dev/null +++ b/azure-pipelines/vs-insertion.yml @@ -0,0 +1,50 @@ +trigger: none # We only want to trigger manually or based on resources +pr: none + +resources: + pipelines: + - pipeline: CI + source: vs-threading + tags: + - Real signed + trigger: + tags: + - Real signed + - auto-insertion + +stages: +- stage: VS + displayName: VS insertion + jobs: + - deployment: insertion + pool: VSEng-ReleasePool + environment: No-Approval + strategy: + runOnce: + deploy: + steps: + - powershell: | + Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" + displayName: Set pipeline name + - template: release-deployment-prep.yml + - download: CI + artifact: VSInsertion-Windows + displayName: Download VSInsertion-Windows artifact + - task: NuGetCommand@2 + displayName: Push CoreXT packages to VS feed + inputs: + command: push + packagesToPush: $(Pipeline.Workspace)/CI/VSInsertion-windows/*.nupkg + publishVstsFeed: 97a41293-2972-4f48-8c0e-05493ae82010 + allowPackageConflicts: true + - task: MicroBuildInsertVsPayload@3 + displayName: Insert VS Payload + inputs: + TeamName: $(TeamName) + TeamEmail: $(TeamEmail) + InsertionPayloadName: $(Build.Repository.Name) $(Build.BuildNumber) + InsertionBuildPolicy: Request Perf DDRITs + AutoCompletePR: true + AutoCompleteMergeStrategy: Squash + - task: MicroBuildCleanup@1 + displayName: Send Telemetry diff --git a/doc/library_with_jtf.md b/doc/library_with_jtf.md index 40ae72c1..37605fc4 100644 --- a/doc/library_with_jtf.md +++ b/doc/library_with_jtf.md @@ -95,7 +95,7 @@ public static class LibrarySettings { get { - if (joinableTaskContext == null) + if (joinableTaskContext is null) { // This self-initializer is for when an app does not have a `JoinableTaskContext` to pass to the library. // Our private instance will only work if this property getter first runs on the main thread of the application @@ -108,7 +108,7 @@ public static class LibrarySettings set { - Assumes.True(joinableTaskContext == null || joinableTaskContext == value, "This property has already been set to another value or is set after its value has been retrieved with a self-created value. Set this property once, before it is used elsewhere."); + Assumes.True(joinableTaskContext is null || joinableTaskContext == value, "This property has already been set to another value or is set after its value has been retrieved with a self-created value. Set this property once, before it is used elsewhere."); joinableTaskContext = value; } } @@ -117,7 +117,7 @@ public static class LibrarySettings This pattern and self-initializer allows all the rest of your library code to assume JTF is always present (so you can use JTF.Run and JTF.RunAsync everywhere w/o feature that JTF will be null), and it mitigates all the deadlocks possible given the host constraints. -Note that when you create your own default instance of JoinableTaskContext (i.e. when the host doesn't), it will consider the thread you're on to be the main thread. If SynchronizationContext.Current != null it will capture it and use it to switch to the main thread when you ask it to (very similar to how VS works today), otherwise any request to SwitchToMainThreadAsync will never switch the thread (since no `SynchronizationContext` was supplied to do so) but otherwise JTF continues to work. +Note that when you create your own default instance of JoinableTaskContext (i.e. when the host doesn't), it will consider the thread you're on to be the main thread. If SynchronizationContext.Current is object it will capture it and use it to switch to the main thread when you ask it to (very similar to how VS works today), otherwise any request to SwitchToMainThreadAsync will never switch the thread (since no `SynchronizationContext` was supplied to do so) but otherwise JTF continues to work. ## Accept `JoinableTaskContext` as a constructor argument diff --git a/global.json b/global.json index 22ddecfc..2d3bd80b 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "3.1.100", - "rollForward": "latestPatch", + "version": "3.1.302", + "rollForward": "patch", "allowPrerelease": false }, "msbuild-sdks": { diff --git a/init.cmd b/init.cmd index 5bef08da..667efabb 100644 --- a/init.cmd +++ b/init.cmd @@ -1,3 +1,20 @@ -@set PS1UnderCmd=1 -powershell.exe -ExecutionPolicy bypass -Command "& '%~dpn0.ps1'" %* -@set PS1UnderCmd= +@echo off +SETLOCAL +set PS1UnderCmd=1 + +:: Get the datetime in a format that can go in a filename. +set _my_datetime=%date%_%time% +set _my_datetime=%_my_datetime: =_% +set _my_datetime=%_my_datetime::=% +set _my_datetime=%_my_datetime:/=_% +set _my_datetime=%_my_datetime:.=_% +set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd + +powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }" + +:: Set environment variables in the parent cmd.exe process. +IF EXIST "%CmdEnvScriptPath%" ( + ENDLOCAL + CALL "%CmdEnvScriptPath%" + DEL "%CmdEnvScriptPath%" +) diff --git a/init.ps1 b/init.ps1 index 2baf7596..a99b95f4 100644 --- a/init.ps1 +++ b/init.ps1 @@ -1,27 +1,36 @@ +#!/usr/bin/env pwsh + <# .SYNOPSIS -Installs dependencies required to build and test the projects in this repository. + Installs dependencies required to build and test the projects in this repository. .DESCRIPTION -This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location, -unless `-InstallLocality machine` is specified. + This MAY not require elevation, as the SDK and runtimes are installed to a per-user location, + unless the `-InstallLocality` switch is specified directing to a per-repo or per-machine location. + See detailed help on that switch for more information. + + The CmdEnvScriptPath environment variable may be optionally set to a path to a cmd shell script to be created (or appended to if it already exists) that will set the environment variables in cmd.exe that are set within the PowerShell environment. + This is used by init.cmd in order to reapply any new environment variables to the parent cmd.exe process that were set in the powershell child process. .PARAMETER InstallLocality -A value indicating whether dependencies should be installed locally to the repo or at a per-user location. -Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. -Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. -Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. -When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. -Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. -Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. + A value indicating whether dependencies should be installed locally to the repo or at a per-user location. + Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. + Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. + Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. + When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. + Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. + Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. .PARAMETER NoPrerequisites -Skips the installation of prerequisite software (e.g. SDKs, tools). + Skips the installation of prerequisite software (e.g. SDKs, tools). +.PARAMETER UpgradePrerequisites + Takes time to install prerequisites even if they are already present in case they need to be upgraded. + No effect if -NoPrerequisites is specified. .PARAMETER NoRestore -Skips the package restore step. + Skips the package restore step. .PARAMETER Signing -Install the MicroBuild signing plugin for building test-signed builds on desktop machines. + Install the MicroBuild signing plugin for building test-signed builds on desktop machines. .PARAMETER OptProf -Install the MicroBuild OptProf plugin for building optimized assemblies on desktop machines. + Install the MicroBuild OptProf plugin for building optimized assemblies on desktop machines. .PARAMETER AccessToken -An optional access token for authenticating to Azure Artifacts authenticated feeds. + An optional access token for authenticating to Azure Artifacts authenticated feeds. #> [CmdletBinding(SupportsShouldProcess=$true)] Param ( @@ -30,6 +39,8 @@ Param ( [Parameter()] [switch]$NoPrerequisites, [Parameter()] + [switch]$UpgradePrerequisites, + [Parameter()] [switch]$NoRestore, [Parameter()] [switch]$Signing, @@ -39,9 +50,20 @@ Param ( [string]$AccessToken ) +$EnvVars = @{} + if (!$NoPrerequisites) { - & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken + & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites & "$PSScriptRoot\tools\Install-DotNetSdk.ps1" -InstallLocality $InstallLocality + if ($LASTEXITCODE -eq 3010) { + Exit 3010 + } + + # The procdump tool and env var is required for dotnet test to collect hang/crash dumps of tests. + # But it only works on Windows. + if ($env:OS -eq 'Windows_NT') { + $EnvVars['PROCDUMP_PATH'] = & "$PSScriptRoot\azure-pipelines\Get-ProcDump.ps1" + } } # Workaround nuget credential provider bug that causes very unreliable package restores on Azure Pipelines @@ -52,15 +74,14 @@ Push-Location $PSScriptRoot try { $HeaderColor = 'Green' - if (!$NoRestore) { + if (!$NoRestore -and $PSCmdlet.ShouldProcess("NuGet packages", "Restore")) { Write-Host "Restoring NuGet packages" -ForegroundColor $HeaderColor - dotnet restore src + dotnet restore if ($lastexitcode -ne 0) { throw "Failure while restoring packages." } } - $EnvVars = @{} $InstallNuGetPkgScriptPath = ".\azure-pipelines\Install-NuGetPackage.ps1" $nugetVerbosity = 'quiet' if ($Verbose) { $nugetVerbosity = 'normal' } @@ -77,7 +98,7 @@ try { $EnvVars['OptProfEnabled'] = '1' } - & "$PSScriptRoot\azure-pipelines\Set-EnvVars.ps1" -Variables $EnvVars | Out-Null + & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars | Out-Null } catch { Write-Error $error[0] diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..36b70ad1 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,13 @@ +[Microsoft.VisualStudio.Threading.Analyzers*/*.cs] + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = suggestion + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = none + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent + +# RS2008: Enable analyzer release tracking +dotnet_diagnostic.RS2008.severity = suggestion diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 82b2c946..c73680b5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,50 +1,11 @@ + + - Debug - - $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\')) - $(RepoRoot)bin\ - $(RepoRoot)obj\ - $(RepoObjPath)\$(MSBuildProjectName)\ - $(RepoBinPath)\$(MSBuildProjectName)\ - $(RepoBinPath)Packages\$(Configuration)\NuGet\ - true - - 8 - enable - - 2.0.58 - true - true - true - snupkg - - Microsoft - Microsoft, VisualStudioExtensibility - © Microsoft Corporation. All rights reserved. - https://github.com/Microsoft/vs-threading - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - PackageIcon.png - MIT - - - - - - - + - - - - - - - https://github.com/microsoft/vs-threading/releases/tag/v$(Version) - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index c3e1ac7f..e7edee55 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,8 +1,3 @@ - - cobertura - [xunit.*]*,[Microsoft.CodeAnalysis*]* - - $(OutputPath)/ - + diff --git a/src/IsolatedTestHost/IsolatedTestHost.csproj b/src/IsolatedTestHost/IsolatedTestHost.csproj deleted file mode 100644 index 0ba028ab..00000000 --- a/src/IsolatedTestHost/IsolatedTestHost.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net472 - true - $(NoWarn);CS1591 - ..\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.ruleset - false - - - - - - - diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/AssemblyInfo.cs index 17743930..18c0aa28 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/AssemblyInfo.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Resources; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpCommonInterest.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpCommonInterest.cs index ecb1fc9d..68aa2015 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpCommonInterest.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpCommonInterest.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -38,10 +39,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// internal static bool ShouldIgnoreContext(SyntaxNodeAnalysisContext context) { - var namespaceDeclaration = context.Node.FirstAncestorOrSelf(); - if (namespaceDeclaration != null) + NamespaceDeclarationSyntax? namespaceDeclaration = context.Node.FirstAncestorOrSelf(); + if (namespaceDeclaration is object) { - foreach (var trivia in namespaceDeclaration.NamespaceKeyword.GetAllTrivia()) + foreach (SyntaxTrivia trivia in namespaceDeclaration.NamespaceKeyword.GetAllTrivia()) { const string autoGeneratedKeyword = @""; if (trivia.FullSpan.Length > autoGeneratedKeyword.Length @@ -62,12 +63,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers IEnumerable problematicMethods, bool ignoreIfInsideAnonymousDelegate = false) { - if (descriptor == null) + if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } - if (memberAccessSyntax == null) + if (memberAccessSyntax is null) { return; } @@ -77,7 +78,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return; } - if (ignoreIfInsideAnonymousDelegate && context.Node.FirstAncestorOrSelf() != null) + if (ignoreIfInsideAnonymousDelegate && context.Node.FirstAncestorOrSelf() is object) { // We do not analyze JTF.Run inside anonymous functions because // they are so often used as callbacks where the signature is constrained. @@ -90,16 +91,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return; } - var typeReceiver = context.SemanticModel.GetTypeInfo(memberAccessSyntax.Expression).Type; - if (typeReceiver != null) + ITypeSymbol? typeReceiver = context.SemanticModel.GetTypeInfo(memberAccessSyntax.Expression).Type; + if (typeReceiver is object) { - foreach (var item in problematicMethods) + foreach (CommonInterest.SyncBlockingMethod item in problematicMethods) { if (memberAccessSyntax.Name.Identifier.Text == item.Method.Name && typeReceiver.Name == item.Method.ContainingType.Name && typeReceiver.BelongsToNamespace(item.Method.ContainingType.Namespace)) { - var location = memberAccessSyntax.Name.GetLocation(); + Location? location = memberAccessSyntax.Name.GetLocation(); context.ReportDiagnostic(Diagnostic.Create(descriptor, location)); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpUtils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpUtils.cs index 3e06b362..a08527f8 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpUtils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpUtils.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -22,7 +23,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static ExpressionSyntax IsolateMethodName(InvocationExpressionSyntax invocation) { - if (invocation == null) + if (invocation is null) { throw new ArgumentNullException(nameof(invocation)); } @@ -39,7 +40,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// The containing function, and metadata for it. internal static ContainingFunctionData GetContainingFunction(CSharpSyntaxNode syntaxNode) { - while (syntaxNode != null) + while (syntaxNode is object) { if (syntaxNode is SimpleLambdaExpressionSyntax simpleLambda) { @@ -75,7 +76,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool IsOnLeftHandOfAssignment(SyntaxNode syntaxNode) { SyntaxNode? parent = null; - while ((parent = syntaxNode.Parent) != null) + while ((parent = syntaxNode.Parent) is object) { if (parent is AssignmentExpressionSyntax assignment) { @@ -90,26 +91,26 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool IsAssignedWithin(SyntaxNode container, SemanticModel semanticModel, ISymbol variable, CancellationToken cancellationToken) { - if (semanticModel == null) + if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } - if (variable == null) + if (variable is null) { throw new ArgumentNullException(nameof(variable)); } - if (container == null) + if (container is null) { return false; } - foreach (var node in container.DescendantNodesAndSelf(n => !(n is AnonymousFunctionExpressionSyntax))) + foreach (SyntaxNode? node in container.DescendantNodesAndSelf(n => !(n is AnonymousFunctionExpressionSyntax))) { if (node is AssignmentExpressionSyntax assignment) { - var assignedSymbol = semanticModel.GetSymbolInfo(assignment.Left, cancellationToken).Symbol; + ISymbol? assignedSymbol = semanticModel.GetSymbolInfo(assignment.Left, cancellationToken).Symbol; if (variable.Equals(assignedSymbol)) { return true; @@ -122,12 +123,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static MemberAccessExpressionSyntax MemberAccess(IReadOnlyList qualifiers, SimpleNameSyntax simpleName) { - if (qualifiers == null) + if (qualifiers is null) { throw new ArgumentNullException(nameof(qualifiers)); } - if (simpleName == null) + if (simpleName is null) { throw new ArgumentNullException(nameof(simpleName)); } @@ -140,7 +141,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers ExpressionSyntax result = SyntaxFactory.IdentifierName(qualifiers[0]); for (int i = 1; i < qualifiers.Count; i++) { - var rightSide = SyntaxFactory.IdentifierName(qualifiers[i]); + IdentifierNameSyntax? rightSide = SyntaxFactory.IdentifierName(qualifiers[i]); result = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, result, rightSide); } @@ -152,7 +153,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// internal static bool IsWithinNameOf([NotNullWhen(true)] SyntaxNode? syntaxNode) { - var invocation = syntaxNode?.FirstAncestorOrSelf(); + InvocationExpressionSyntax? invocation = syntaxNode?.FirstAncestorOrSelf(); return invocation is object && (invocation.Expression as IdentifierNameSyntax)?.Identifier.Text == "nameof" && invocation.ArgumentList.Arguments.Count == 1; @@ -160,14 +161,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal override Location? GetLocationOfBaseTypeName(INamedTypeSymbol symbol, INamedTypeSymbol baseType, Compilation compilation, CancellationToken cancellationToken) { - foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) + foreach (SyntaxReference? syntaxReference in symbol.DeclaringSyntaxReferences) { - var syntaxNode = syntaxReference.GetSyntax(cancellationToken); + SyntaxNode? syntaxNode = syntaxReference.GetSyntax(cancellationToken); if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax) { if (compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree) is { } semanticModel) { - foreach (var baseTypeSyntax in typeDeclarationSyntax.BaseList.Types) + foreach (BaseTypeSyntax? baseTypeSyntax in typeDeclarationSyntax.BaseList.Types) { SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(baseTypeSyntax.Type, cancellationToken); if (Equals(baseTypeSymbolInfo.Symbol, baseType)) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs index c0425926..01848cb7 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs index 8a211454..5580c313 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD011UseAsyncLazyAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD011UseAsyncLazyAnalyzer.cs index 13a56721..33f4c8e3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD011UseAsyncLazyAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD011UseAsyncLazyAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD012SpecifyJtfWhereAllowed.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD012SpecifyJtfWhereAllowed.cs index 32cbaeaf..2143f395 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD012SpecifyJtfWhereAllowed.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD012SpecifyJtfWhereAllowed.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs index f8406df3..94ee09e1 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs index 1d8ac7c2..3a40bbe9 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD108AssertThreadRequirementUnconditionally.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs index 20d0a859..6e620baa 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs index 2a7ab0c8..dbaf87ba 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj index 6ddbd362..1aee7597 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj @@ -1,9 +1,6 @@  netstandard1.3 - true - $(NoWarn);CS1591 - ..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset Microsoft.VisualStudio.Threading.Analyzers false diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD002UseJtfRunAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD002UseJtfRunAnalyzer.cs index a12a9188..cdf1fa7b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD002UseJtfRunAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD002UseJtfRunAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -63,18 +60,18 @@ namespace Microsoft.VisualStudio.Threading.Analyzers context.RegisterCompilationStartAction(compilationContext => { - var taskSymbol = compilationContext.Compilation.GetTypeByMetadataName(Types.Task.FullName); - if (taskSymbol != null) + INamedTypeSymbol? taskSymbol = compilationContext.Compilation.GetTypeByMetadataName(Types.Task.FullName); + if (taskSymbol is object) { compilationContext.RegisterCodeBlockStartAction(codeBlockContext => { // We want to scan properties and methods that do not return Task or Task. var methodSymbol = codeBlockContext.OwningSymbol as IMethodSymbol; var propertySymbol = codeBlockContext.OwningSymbol as IPropertySymbol; - if (propertySymbol != null || (methodSymbol != null && !methodSymbol.HasAsyncCompatibleReturnType())) + if (propertySymbol is object || (methodSymbol is object && !methodSymbol.HasAsyncCompatibleReturnType())) { - codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => this.AnalyzeInvocation(c, taskSymbol)), SyntaxKind.InvocationExpression); - codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => this.AnalyzeMemberAccess(c, taskSymbol)), SyntaxKind.SimpleMemberAccessExpression); + codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AnalyzeInvocation(c, taskSymbol)), SyntaxKind.InvocationExpression); + codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AnalyzeMemberAccess(c, taskSymbol)), SyntaxKind.SimpleMemberAccessExpression); } }); } @@ -96,43 +93,23 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return null; } - private void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol) - { - var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node; - this.InspectMemberAccess( - context, - invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax, - CommonInterest.ProblematicSyncBlockingMethods, - taskSymbol); - } - - private void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol) - { - var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node; - this.InspectMemberAccess( - context, - memberAccessSyntax, - CommonInterest.SyncBlockingProperties, - taskSymbol); - } - - private void InspectMemberAccess( + private static void InspectMemberAccess( SyntaxNodeAnalysisContext context, MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable problematicMethods, INamedTypeSymbol taskSymbol) { - if (memberAccessSyntax == null) + if (memberAccessSyntax is null) { return; } // Are we in the context of an anonymous function that is passed directly in as an argument to another method? - var anonymousFunctionSyntax = context.Node.FirstAncestorOrSelf(); + AnonymousFunctionExpressionSyntax? anonymousFunctionSyntax = context.Node.FirstAncestorOrSelf(); var anonFuncAsArgument = anonymousFunctionSyntax?.Parent as ArgumentSyntax; var invocationPassingExpression = anonFuncAsArgument?.Parent?.Parent as InvocationExpressionSyntax; var invokedMemberAccess = invocationPassingExpression?.Expression as MemberAccessExpressionSyntax; - if (invokedMemberAccess?.Name != null) + if (invokedMemberAccess?.Name is object) { // Does the anonymous function appear as the first argument to Task.ContinueWith? var invokedMemberSymbol = context.SemanticModel.GetSymbolInfo(invokedMemberAccess.Name, context.CancellationToken).Symbol as IMethodSymbol; @@ -141,8 +118,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers invocationPassingExpression?.ArgumentList?.Arguments.FirstOrDefault() == anonFuncAsArgument) { // Does the member access being analyzed belong to the Task that just completed? - var firstParameter = GetFirstParameter(anonymousFunctionSyntax); - if (firstParameter != null) + ParameterSyntax? firstParameter = GetFirstParameter(anonymousFunctionSyntax); + if (firstParameter is object) { // Are we accessing a member of the completed task? ISymbol invokedObjectSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax.Expression, context.CancellationToken).Symbol; @@ -158,5 +135,25 @@ namespace Microsoft.VisualStudio.Threading.Analyzers CSharpCommonInterest.InspectMemberAccess(context, memberAccessSyntax, Descriptor, problematicMethods); } + + private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol) + { + var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node; + InspectMemberAccess( + context, + invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax, + CommonInterest.ProblematicSyncBlockingMethods, + taskSymbol); + } + + private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol) + { + var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node; + InspectMemberAccess( + context, + memberAccessSyntax, + CommonInterest.SyncBlockingProperties, + taskSymbol); + } } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs index 6282783b..223f648d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -52,11 +49,6 @@ namespace Microsoft.VisualStudio.Threading.Analyzers defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); - private static readonly IReadOnlyCollection DoNotPassTypesInSearchForAnonFuncInvocation = new[] - { - typeof(MethodDeclarationSyntax), - }; - /// public override ImmutableArray SupportedDiagnostics { @@ -84,7 +76,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var arrowExpressionClause = (ArrowExpressionClauseSyntax)context.Node; if (arrowExpressionClause.Parent is MethodDeclarationSyntax) { - var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(arrowExpressionClause.Expression, context, context.CancellationToken); + Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(arrowExpressionClause.Expression, context, context.CancellationToken); if (diagnostic is object) { context.ReportDiagnostic(diagnostic); @@ -97,8 +89,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var lambdaExpression = (LambdaExpressionSyntax)context.Node; if (lambdaExpression.Body is ExpressionSyntax expression) { - var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(expression, context, context.CancellationToken); - if (diagnostic != null) + Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(expression, context, context.CancellationToken); + if (diagnostic is object) { context.ReportDiagnostic(diagnostic); } @@ -108,8 +100,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeReturnStatement(SyntaxNodeAnalysisContext context) { var returnStatement = (ReturnStatementSyntax)context.Node; - var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(returnStatement.Expression, context, context.CancellationToken); - if (diagnostic != null) + Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(returnStatement.Expression, context, context.CancellationToken); + if (diagnostic is object) { context.ReportDiagnostic(diagnostic); } @@ -118,8 +110,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context) { AwaitExpressionSyntax awaitExpressionSyntax = (AwaitExpressionSyntax)context.Node; - var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(awaitExpressionSyntax.Expression, context, context.CancellationToken); - if (diagnostic != null) + Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(awaitExpressionSyntax.Expression, context, context.CancellationToken); + if (diagnostic is object) { context.ReportDiagnostic(diagnostic); } @@ -127,14 +119,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private Diagnostic? AnalyzeAwaitedOrReturnedExpression(ExpressionSyntax expressionSyntax, SyntaxNodeAnalysisContext context, CancellationToken cancellationToken) { - if (expressionSyntax == null) + if (expressionSyntax is null) { return null; } // Get the semantic model for the SyntaxTree for the given ExpressionSyntax, since it *may* not be in the same syntax tree // as the original context.Node. - if (!context.TryGetNewOrExistingSemanticModel(expressionSyntax.SyntaxTree, out var semanticModel)) + if (!context.TryGetNewOrExistingSemanticModel(expressionSyntax.SyntaxTree, out SemanticModel? semanticModel)) { return null; } @@ -174,12 +166,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return null; } - foreach (var syntaxReference in fieldSymbol.DeclaringSyntaxReferences) + foreach (SyntaxReference? syntaxReference in fieldSymbol.DeclaringSyntaxReferences) { if (syntaxReference.GetSyntax(cancellationToken) is VariableDeclaratorSyntax declarationSyntax) { if (declarationSyntax.Initializer?.Value is InvocationExpressionSyntax invocationSyntax && - invocationSyntax.Expression != null) + invocationSyntax.Expression is object) { if (!context.Compilation.ContainsSyntaxTree(invocationSyntax.SyntaxTree)) { @@ -188,7 +180,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } // Whitelist Task.From*() methods. - if (!context.TryGetNewOrExistingSemanticModel(invocationSyntax.SyntaxTree, out var declarationSemanticModel)) + if (!context.TryGetNewOrExistingSemanticModel(invocationSyntax.SyntaxTree, out SemanticModel? declarationSemanticModel)) { return null; } @@ -203,12 +195,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } else if (declarationSyntax.Initializer?.Value is MemberAccessExpressionSyntax memberAccessSyntax && memberAccessSyntax.Expression is object) { - if (!context.TryGetNewOrExistingSemanticModel(memberAccessSyntax.SyntaxTree, out var declarationSemanticModel)) + if (!context.TryGetNewOrExistingSemanticModel(memberAccessSyntax.SyntaxTree, out SemanticModel? declarationSemanticModel)) { return null; } - var definition = declarationSemanticModel.GetSymbolInfo(memberAccessSyntax, cancellationToken).Symbol; + ISymbol? definition = declarationSemanticModel.GetSymbolInfo(memberAccessSyntax, cancellationToken).Symbol; if (definition is IFieldSymbol field) { // Whitelist the TplExtensions.CompletedTask and related fields. @@ -237,7 +229,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers if (Utils.IsTask(methodSymbol.ReturnType) && expressionSyntax is InvocationExpressionSyntax invocationExpressionSyntax) { // Consider all arguments - var expressionsToConsider = invocationExpressionSyntax.ArgumentList.Arguments.Select(a => a.Expression); + IEnumerable? expressionsToConsider = invocationExpressionSyntax.ArgumentList.Arguments.Select(a => a.Expression); // Consider the implicit first argument when this method is invoked as an extension method. if (methodSymbol.IsExtensionMethod && invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax invokedMember) @@ -248,7 +240,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } } - return expressionsToConsider.Select(e => this.AnalyzeAwaitedOrReturnedExpression(e, context, cancellationToken)).FirstOrDefault(r => r != null); + return expressionsToConsider.Select(e => this.AnalyzeAwaitedOrReturnedExpression(e, context, cancellationToken)).FirstOrDefault(r => r is object); } return null; @@ -262,7 +254,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } // Report warning if the task was not initialized within the current delegate or lambda expression - var containingFunc = CSharpUtils.GetContainingFunction(expressionSyntax); + CSharpUtils.ContainingFunctionData containingFunc = CSharpUtils.GetContainingFunction(expressionSyntax); if (containingFunc.BlockOrExpression is BlockSyntax delegateBlock) { if (dataflowAnalysisCompatibleVariable) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs index e684ee1c..a225d69f 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -114,7 +117,7 @@ var mainThreadAssertingMethods = CommonInterest.ReadMethods(compilationStartContext.Options, CommonInterest.FileNamePatternForMethodsThatAssertMainThread, compilationStartContext.CancellationToken).ToImmutableArray(); var mainThreadSwitchingMethods = CommonInterest.ReadMethods(compilationStartContext.Options, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, compilationStartContext.CancellationToken).ToImmutableArray(); var membersRequiringMainThread = CommonInterest.ReadTypesAndMembers(compilationStartContext.Options, CommonInterest.FileNamePatternForMembersRequiringMainThread, compilationStartContext.CancellationToken).ToImmutableArray(); - var diagnosticProperties = ImmutableDictionary.Empty + ImmutableDictionary? diagnosticProperties = ImmutableDictionary.Empty .Add(CommonInterest.FileNamePatternForMethodsThatAssertMainThread.ToString(), string.Join("\n", mainThreadAssertingMethods)) .Add(CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread.ToString(), string.Join("\n", mainThreadSwitchingMethods)); @@ -145,9 +148,9 @@ compilationStartContext.RegisterCompilationEndAction(compilationEndContext => { - var calleeToCallerMap = CreateCalleeToCallerMap(callerToCalleeMap); - var transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsAssertingUIThreadRequirement, calleeToCallerMap); - foreach (var implicitUserMethod in transitiveClosureOfMainThreadRequiringMethods.Except(methodsDeclaringUIThreadRequirement)) + Dictionary>? calleeToCallerMap = CreateCalleeToCallerMap(callerToCalleeMap); + HashSet? transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsAssertingUIThreadRequirement, calleeToCallerMap); + foreach (IMethodSymbol? implicitUserMethod in transitiveClosureOfMainThreadRequiringMethods.Except(methodsDeclaringUIThreadRequirement)) { var reportSites = from info in callerToCalleeMap[implicitUserMethod] where transitiveClosureOfMainThreadRequiringMethods.Contains(info.MethodSymbol) @@ -156,7 +159,7 @@ foreach (var site in reportSites) { bool isAsync = Utils.IsAsyncReady(implicitUserMethod); - var descriptor = isAsync ? DescriptorAsync : DescriptorSync; + DiagnosticDescriptor? descriptor = isAsync ? DescriptorAsync : DescriptorSync; string calleeName = Utils.GetFullName(site.CalleeMethod); var formattingArgs = isAsync ? new object[] { calleeName } : new object[] { calleeName, mainThreadAssertingMethods.FirstOrDefault() }; Diagnostic diagnostic = Diagnostic.Create( @@ -177,12 +180,12 @@ void MarkMethod(IMethodSymbol method) { - if (result.Add(method) && calleeToCallerMap.TryGetValue(method, out var callers)) + if (result.Add(method) && calleeToCallerMap.TryGetValue(method, out List? callers)) { // If this is an async method, do *not* propagate its thread affinity to its callers. if (!Utils.IsAsyncCompatibleReturnType(method.ReturnType)) { - foreach (var caller in callers) + foreach (CallInfo caller in callers) { MarkMethod(caller.MethodSymbol); } @@ -190,7 +193,7 @@ } } - foreach (var method in methodsRequiringUIThread) + foreach (IMethodSymbol? method in methodsRequiringUIThread) { MarkMethod(method); } @@ -207,7 +210,7 @@ IMethodSymbol? GetPropertyAccessor(IPropertySymbol? propertySymbol) { - if (propertySymbol != null) + if (propertySymbol is object) { return CSharpUtils.IsOnLeftHandOfAssignment(context.Node) ? propertySymbol.SetMethod @@ -251,12 +254,12 @@ { var result = new Dictionary>(); - foreach (var item in callerToCalleeMap) + foreach (KeyValuePair> item in callerToCalleeMap) { - var caller = item.Key; - foreach (var callee in item.Value) + IMethodSymbol? caller = item.Key; + foreach (CallInfo callee in item.Value) { - if (!result.TryGetValue(callee.MethodSymbol, out var callers)) + if (!result.TryGetValue(callee.MethodSymbol, out List? callers)) { result[callee.MethodSymbol] = callers = new List(); } @@ -317,10 +320,10 @@ { var invocationSyntax = (InvocationExpressionSyntax)context.Node; var invokedMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol; - if (invokedMethod != null) + if (invokedMethod is object) { - var methodDeclaration = context.Node.FirstAncestorOrSelf(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind())); - if (methodDeclaration != null) + SyntaxNode? methodDeclaration = context.Node.FirstAncestorOrSelf(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind())); + if (methodDeclaration is object) { bool assertsMainThread = this.MainThreadAssertingMethods.Contains(invokedMethod); bool switchesToMainThread = this.MainThreadSwitchingMethods.Contains(invokedMethod); @@ -348,11 +351,11 @@ } // The diagnostic (if any) should underline the method name only. - var focusedNode = invocationSyntax.Expression; + ExpressionSyntax? focusedNode = invocationSyntax.Expression; focusedNode = (focusedNode as MemberAccessExpressionSyntax)?.Name ?? focusedNode; if (!this.AnalyzeMemberWithinContext(invokedMethod.ContainingType, invokedMethod, context, focusedNode.GetLocation())) { - foreach (var iface in invokedMethod.FindInterfacesImplemented()) + foreach (ITypeSymbol? iface in invokedMethod.FindInterfacesImplemented()) { if (this.AnalyzeMemberWithinContext(iface, invokedMethod, context, focusedNode.GetLocation())) { @@ -368,7 +371,7 @@ { var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node; var property = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IPropertySymbol; - if (property != null) + if (property is object) { this.AnalyzeMemberWithinContext(property.ContainingType, property, context, memberAccessSyntax.Name.GetLocation()); } @@ -378,7 +381,7 @@ { var castSyntax = (CastExpressionSyntax)context.Node; var type = context.SemanticModel.GetSymbolInfo(castSyntax.Type, context.CancellationToken).Symbol as ITypeSymbol; - if (type != null && IsObjectLikelyToBeCOMObject(type)) + if (type is object && IsObjectLikelyToBeCOMObject(type)) { this.AnalyzeMemberWithinContext(type, null, context); } @@ -388,7 +391,7 @@ { var asSyntax = (BinaryExpressionSyntax)context.Node; var type = context.SemanticModel.GetSymbolInfo(asSyntax.Right, context.CancellationToken).Symbol as ITypeSymbol; - if (type != null && IsObjectLikelyToBeCOMObject(type)) + if (type is object && IsObjectLikelyToBeCOMObject(type)) { Location asAndRightSide = Location.Create(context.Node.SyntaxTree, TextSpan.FromBounds(asSyntax.OperatorToken.Span.Start, asSyntax.Right.Span.End)); this.AnalyzeMemberWithinContext(type, null, context, asAndRightSide); @@ -398,15 +401,16 @@ internal void AnalyzeIsPattern(SyntaxNodeAnalysisContext context) { var patternSyntax = (IsPatternExpressionSyntax)context.Node; - if (patternSyntax.Pattern is DeclarationPatternSyntax declarationPatternSyntax && declarationPatternSyntax.Type != null) + if (patternSyntax.Pattern is DeclarationPatternSyntax declarationPatternSyntax && declarationPatternSyntax.Type is object) { var type = context.SemanticModel.GetSymbolInfo(declarationPatternSyntax.Type, context.CancellationToken).Symbol as ITypeSymbol; - if (type != null && IsObjectLikelyToBeCOMObject(type)) + if (type is object && IsObjectLikelyToBeCOMObject(type)) { Location isAndTypeSide = Location.Create( context.Node.SyntaxTree, - TextSpan.FromBounds(patternSyntax.IsKeyword.SpanStart, - declarationPatternSyntax.Type.Span.End)); + TextSpan.FromBounds( + patternSyntax.IsKeyword.SpanStart, + declarationPatternSyntax.Type.Span.End)); this.AnalyzeMemberWithinContext(type, null, context, isAndTypeSide); } } @@ -421,7 +425,7 @@ /// private static bool IsObjectLikelyToBeCOMObject(ITypeSymbol typeSymbol) { - if (typeSymbol == null) + if (typeSymbol is null) { throw new ArgumentNullException(nameof(typeSymbol)); } @@ -435,7 +439,7 @@ private bool AnalyzeMemberWithinContext(ITypeSymbol type, ISymbol? symbol, SyntaxNodeAnalysisContext context, Location? focusDiagnosticOn = null) { - if (type == null) + if (type is null) { throw new ArgumentNullException(nameof(type)); } @@ -445,18 +449,18 @@ if (requiresUIThread) { - var threadingContext = ThreadingContext.Unknown; - var methodDeclaration = context.Node.FirstAncestorOrSelf(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind())); - if (methodDeclaration != null) + ThreadingContext threadingContext = ThreadingContext.Unknown; + SyntaxNode? methodDeclaration = context.Node.FirstAncestorOrSelf(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind())); + if (methodDeclaration is object) { threadingContext = this.methodDeclarationNodes.GetValueOrDefault(methodDeclaration); } if (threadingContext != ThreadingContext.MainThread) { - var function = CSharpUtils.GetContainingFunction((CSharpSyntaxNode)context.Node); + CSharpUtils.ContainingFunctionData function = CSharpUtils.GetContainingFunction((CSharpSyntaxNode)context.Node); Location location = focusDiagnosticOn ?? context.Node.GetLocation(); - var descriptor = function.IsAsync ? DescriptorAsync : DescriptorSync; + DiagnosticDescriptor? descriptor = function.IsAsync ? DescriptorAsync : DescriptorSync; var formattingArgs = function.IsAsync ? new object[] { type.Name } : new object[] { type.Name, this.MainThreadAssertingMethods.FirstOrDefault() }; context.ReportDiagnostic(Diagnostic.Create(descriptor, location, this.DiagnosticProperties, formattingArgs)); return true; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzer.cs index 97830ef3..88a35955 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -48,15 +51,14 @@ var methodSymbol = ctxt.OwningSymbol as IMethodSymbol; if (!methodSymbol.HasAsyncCompatibleReturnType() && !Utils.IsPublic(methodSymbol) && !Utils.IsEntrypointMethod(methodSymbol, ctxt.SemanticModel, ctxt.CancellationToken) && !methodSymbol.FindInterfacesImplemented().Any(Utils.IsPublic)) { - var methodAnalyzer = new MethodAnalyzer(); - ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression); + ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression); } }); } - private class MethodAnalyzer + private static class MethodAnalyzer { - internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context) + internal static void AnalyzeInvocation(SyntaxNodeAnalysisContext context) { var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node; CSharpCommonInterest.InspectMemberAccess( diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD103UseAsyncOptionAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD103UseAsyncOptionAnalyzer.cs index 5912483c..e64c5793 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD103UseAsyncOptionAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD103UseAsyncOptionAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -67,15 +70,14 @@ context.RegisterCodeBlockStartAction(ctxt => { - var methodAnalyzer = new MethodAnalyzer(); - ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression); - ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzePropertyGetter), SyntaxKind.SimpleMemberAccessExpression); + ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression); + ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzePropertyGetter), SyntaxKind.SimpleMemberAccessExpression); }); } private class MethodAnalyzer { - internal void AnalyzePropertyGetter(SyntaxNodeAnalysisContext context) + internal static void AnalyzePropertyGetter(SyntaxNodeAnalysisContext context) { var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node; if (IsInTaskReturningMethodOrDelegate(context)) @@ -84,7 +86,7 @@ } } - internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context) + internal static void AnalyzeInvocation(SyntaxNodeAnalysisContext context) { if (IsInTaskReturningMethodOrDelegate(context)) { @@ -100,19 +102,19 @@ // Also consider all method calls to check for Async-suffixed alternatives. ExpressionSyntax invokedMethodName = CSharpUtils.IsolateMethodName(invocationExpressionSyntax); - var symbolInfo = context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax, context.CancellationToken); + SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax, context.CancellationToken); var methodSymbol = symbolInfo.Symbol as IMethodSymbol; - if (methodSymbol != null && !methodSymbol.Name.EndsWith(VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix) && + if (methodSymbol is object && !methodSymbol.Name.EndsWith(VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix, StringComparison.CurrentCulture) && !(methodSymbol.ReturnType?.Name == nameof(Task) && methodSymbol.ReturnType.BelongsToNamespace(Namespaces.SystemThreadingTasks))) { string asyncMethodName = methodSymbol.Name + VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix; - var symbols = context.SemanticModel.LookupSymbols( + ImmutableArray symbols = context.SemanticModel.LookupSymbols( invocationExpressionSyntax.Expression.GetLocation().SourceSpan.Start, methodSymbol.ContainingType, asyncMethodName, includeReducedExtensionMethods: true); - foreach (var s in symbols) + foreach (ISymbol? s in symbols) { if (s is IMethodSymbol m && !m.IsObsolete() @@ -121,7 +123,7 @@ && Utils.HasAsyncCompatibleReturnType(m)) { // An async alternative exists. - var properties = ImmutableDictionary.Empty + ImmutableDictionary? properties = ImmutableDictionary.Empty .Add(AsyncMethodKeyName, asyncMethodName); Diagnostic diagnostic = Diagnostic.Create( @@ -157,47 +159,47 @@ // We want to scan invocations that occur inside Task and Task-returning delegates or methods. // That is: methods that either are or could be made async. IMethodSymbol? methodSymbol = null; - var anonymousFunc = context.Node.FirstAncestorOrSelf(); - if (anonymousFunc != null) + AnonymousFunctionExpressionSyntax? anonymousFunc = context.Node.FirstAncestorOrSelf(); + if (anonymousFunc is object) { - var symbolInfo = context.SemanticModel.GetSymbolInfo(anonymousFunc, context.CancellationToken); + SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(anonymousFunc, context.CancellationToken); methodSymbol = symbolInfo.Symbol as IMethodSymbol; } else { - var methodDecl = context.Node.FirstAncestorOrSelf(); - if (methodDecl != null) + MethodDeclarationSyntax? methodDecl = context.Node.FirstAncestorOrSelf(); + if (methodDecl is object) { methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDecl, context.CancellationToken); } } - var returnType = methodSymbol?.ReturnType; + ITypeSymbol? returnType = methodSymbol?.ReturnType; return returnType?.Name == nameof(Task) && returnType.BelongsToNamespace(Namespaces.SystemThreadingTasks); } private static bool InspectMemberAccess(SyntaxNodeAnalysisContext context, [NotNullWhen(true)] MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable problematicMethods) { - if (memberAccessSyntax == null) + if (memberAccessSyntax is null) { return false; } - var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol; - if (memberSymbol != null) + ISymbol? memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol; + if (memberSymbol is object) { - foreach (var item in problematicMethods) + foreach (CommonInterest.SyncBlockingMethod item in problematicMethods) { if (item.Method.IsMatch(memberSymbol)) { - var location = memberAccessSyntax.Name.GetLocation(); - var properties = ImmutableDictionary.Empty - .Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace != null ? string.Join(".", item.ExtensionMethodNamespace) : string.Empty); + Location? location = memberAccessSyntax.Name.GetLocation(); + ImmutableDictionary? properties = ImmutableDictionary.Empty + .Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace is object ? string.Join(".", item.ExtensionMethodNamespace) : string.Empty); DiagnosticDescriptor descriptor; var messageArgs = new List(2); messageArgs.Add(item.Method.Name); - if (item.AsyncAlternativeMethodName != null) + if (item.AsyncAlternativeMethodName is object) { properties = properties.Add(AsyncMethodKeyName, item.AsyncAlternativeMethodName); descriptor = Descriptor; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD104OfferAsyncOptionAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD104OfferAsyncOptionAnalyzer.cs index 90acd7b4..22bbc29b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD104OfferAsyncOptionAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD104OfferAsyncOptionAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -64,7 +67,7 @@ private void InspectMemberAccess(SyntaxNodeAnalysisContext context, MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable problematicMethods) { - if (memberAccessSyntax == null) + if (memberAccessSyntax is null) { return; } @@ -75,7 +78,7 @@ return; } - if (context.Node.FirstAncestorOrSelf() != null) + if (context.Node.FirstAncestorOrSelf() is object) { // We do not analyze JTF.Run inside anonymous functions because // they are so often used as callbacks where the signature is constrained. @@ -88,14 +91,14 @@ return; } - var invokedMember = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol; - if (invokedMember != null) + ISymbol? invokedMember = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol; + if (invokedMember is object) { - foreach (var item in problematicMethods) + foreach (CommonInterest.SyncBlockingMethod item in problematicMethods) { if (item.Method.IsMatch(invokedMember)) { - var location = memberAccessSyntax.Name.GetLocation(); + Location? location = memberAccessSyntax.Name.GetLocation(); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); this.diagnosticReported = true; } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzer.cs index 09e54716..5980e459 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -63,7 +60,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeNode(SyntaxNodeAnalysisContext context) { var usingStatement = (UsingStatementSyntax)context.Node; - if (usingStatement.Expression != null) + if (usingStatement.Expression is object) { TypeInfo expressionTypeInfo = context.SemanticModel.GetTypeInfo(usingStatement.Expression, context.CancellationToken); ITypeSymbol expressionType = expressionTypeInfo.Type; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs index c91c312a..ee9f3cac 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD110ObserveResultOfAsyncCallsAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -48,12 +51,12 @@ if (invocation.Parent?.GetType().Equals(typeof(ExpressionStatementSyntax)) ?? false) { var methodSymbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol; - var returnedSymbol = methodSymbol?.ReturnType; + ITypeSymbol? returnedSymbol = methodSymbol?.ReturnType; if (returnedSymbol?.Name == Types.Task.TypeName && returnedSymbol.BelongsToNamespace(Types.Task.Namespace)) { if (!CSharpUtils.GetContainingFunction(invocation).IsAsync) { - var location = (CSharpUtils.IsolateMethodName(invocation) ?? invocation.Expression).GetLocation(); + Location? location = (CSharpUtils.IsolateMethodName(invocation) ?? invocation.Expression).GetLocation(); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/AssemblyInfo.cs index 7c99ade8..5ab05e47 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/AssemblyInfo.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Resources; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs index bec664e1..f0950ae2 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -22,7 +25,7 @@ { internal static async Task> ReadMethodsAsync(CodeFixContext codeFixContext, Regex fileNamePattern, CancellationToken cancellationToken) { - var result = ImmutableArray.CreateBuilder(); + ImmutableArray.Builder? result = ImmutableArray.CreateBuilder(); foreach (string line in await ReadAdditionalFilesAsync(codeFixContext.Document.Project.AdditionalDocuments, fileNamePattern, cancellationToken)) { result.Add(ParseAdditionalFileMethodLine(line)); @@ -33,24 +36,24 @@ internal static async Task> ReadAdditionalFilesAsync(IEnumerable additionalFiles, Regex fileNamePattern, CancellationToken cancellationToken) { - if (additionalFiles == null) + if (additionalFiles is null) { throw new ArgumentNullException(nameof(additionalFiles)); } - if (fileNamePattern == null) + if (fileNamePattern is null) { throw new ArgumentNullException(nameof(fileNamePattern)); } - var docs = from doc in additionalFiles.OrderBy(x => x.FilePath, StringComparer.Ordinal) + IEnumerable? docs = from doc in additionalFiles.OrderBy(x => x.FilePath, StringComparer.Ordinal) let fileName = Path.GetFileName(doc.Name) where fileNamePattern.IsMatch(fileName) select doc; - var result = ImmutableArray.CreateBuilder(); - foreach (var doc in docs) + ImmutableArray.Builder? result = ImmutableArray.CreateBuilder(); + foreach (TextDocument? doc in docs) { - var text = await doc.GetTextAsync(cancellationToken); + SourceText? text = await doc.GetTextAsync(cancellationToken); result.AddRange(ReadLinesFromAdditionalFile(text)); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/FixUtils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/FixUtils.cs index 1d63db9d..2b1eaf00 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/FixUtils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/FixUtils.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -20,7 +17,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static class FixUtils { - internal static readonly string BookmarkAnnotationName = "Bookmark"; + internal const string BookmarkAnnotationName = "Bookmark"; internal static AnonymousFunctionExpressionSyntax MakeMethodAsync(this AnonymousFunctionExpressionSyntax method, bool hasReturnValue, SemanticModel semanticModel, CancellationToken cancellationToken) { @@ -33,7 +30,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers AnonymousFunctionExpressionSyntax? updated = null; var simpleLambda = method as SimpleLambdaExpressionSyntax; - if (simpleLambda != null) + if (simpleLambda is object) { updated = simpleLambda .WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)) @@ -41,7 +38,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } var parentheticalLambda = method as ParenthesizedLambdaExpressionSyntax; - if (parentheticalLambda != null) + if (parentheticalLambda is object) { updated = parentheticalLambda .WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)) @@ -49,14 +46,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } var anonymousMethod = method as AnonymousMethodExpressionSyntax; - if (anonymousMethod != null) + if (anonymousMethod is object) { updated = anonymousMethod .WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)) .WithBody(UpdateStatementsForAsyncMethod(anonymousMethod.Body, semanticModel, hasReturnValue, cancellationToken)); } - if (updated == null) + if (updated is null) { throw new NotSupportedException(); } @@ -80,12 +77,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// internal static async Task> MakeMethodAsync(this MethodDeclarationSyntax method, Document document, CancellationToken cancellationToken = default(CancellationToken)) { - if (method == null) + if (method is null) { throw new ArgumentNullException(nameof(method)); } - if (document == null) + if (document is null) { throw new ArgumentNullException(nameof(document)); } @@ -98,7 +95,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers DocumentId documentId = document.Id; SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(method); + IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(method, cancellationToken); bool hasReturnValue; TypeSyntax returnType = method.ReturnType; @@ -162,7 +159,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers // Don't rename entrypoint (i.e. "Main") methods. if (!Utils.IsEntrypointMethod(methodSymbol, semanticModel, cancellationToken)) { - var solution = await Renamer.RenameSymbolAsync( + Solution? solution = await Renamer.RenameSymbolAsync( document.Project.Solution, methodSymbol, newName, @@ -171,7 +168,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers document = solution.GetDocument(document.Id); semanticModel = null; methodSymbol = null; - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); method = (MethodDeclarationSyntax)root.GetAnnotatedNodes(methodBookmark).Single(); } } @@ -188,8 +185,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers foreach (DocumentId docId in annotatedDocumentIds) { document = solution.GetDocument(docId); - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); + SyntaxTree? tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var rewriter = new AwaitCallRewriter(callerAnnotation); root = rewriter.Visit(root); solution = solution.GetDocument(tree).WithSyntaxRoot(root).Project.Solution; @@ -198,12 +195,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers foreach (DocumentId docId in annotatedDocumentIds) { document = solution.GetDocument(docId); - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); - for (var node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault(); node != null; node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault()) + SyntaxTree? tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); + for (SyntaxNode? node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault(); node is object; node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault()) { - var callingMethod = node.FirstAncestorOrSelf(); - if (callingMethod != null) + MethodDeclarationSyntax? callingMethod = node.FirstAncestorOrSelf(); + if (callingMethod is object) { (document, callingMethod) = await MakeMethodAsync(callingMethod, document, cancellationToken).ConfigureAwait(false); @@ -229,8 +226,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers // Make sure we return the latest of everything. document = solution.GetDocument(documentId); - var finalTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var finalRoot = await finalTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + SyntaxTree? finalTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? finalRoot = await finalTree.GetRootAsync(cancellationToken).ConfigureAwait(false); method = (MethodDeclarationSyntax)finalRoot.GetAnnotatedNodes(methodBookmark).Single(); } @@ -240,20 +237,20 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static async Task>> AnnotateAllCallersAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken) { var bookmark = new SyntaxAnnotation(); - var callers = await SymbolFinder.FindCallersAsync(symbol, solution, cancellationToken).ConfigureAwait(false); - var callersByFile = from caller in callers + IEnumerable? callers = await SymbolFinder.FindCallersAsync(symbol, solution, cancellationToken).ConfigureAwait(false); + IEnumerable>? callersByFile = from caller in callers from location in caller.Locations group location by location.SourceTree into file select file; var updatedDocs = new List(); - foreach (var callerByFile in callersByFile) + foreach (IGrouping? callerByFile in callersByFile) { - var root = await callerByFile.Key.GetRootAsync(cancellationToken).ConfigureAwait(false); - foreach (var caller in callerByFile) + SyntaxNode? root = await callerByFile.Key.GetRootAsync(cancellationToken).ConfigureAwait(false); + foreach (Location? caller in callerByFile) { - var node = root.FindNode(caller.SourceSpan); - var invocation = node.FirstAncestorOrSelf(); - if (invocation != null) + SyntaxNode? node = root.FindNode(caller.SourceSpan); + InvocationExpressionSyntax? invocation = node.FirstAncestorOrSelf(); + if (invocation is object) { root = root.ReplaceNode(invocation, invocation.WithAdditionalAnnotations(bookmark)); } @@ -275,7 +272,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers SyntaxNode root; (bookmark, document, syntaxNode, root) = await BookmarkSyntaxAsync(document, syntaxNode, cancellationToken).ConfigureAwait(false); - var newSyntaxNode = syntaxNodeTransform(syntaxNode); + T? newSyntaxNode = syntaxNodeTransform(syntaxNode); if (!newSyntaxNode.HasAnnotation(bookmark)) { newSyntaxNode = syntaxNode.CopyAnnotationsTo(newSyntaxNode); @@ -293,7 +290,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers where T : SyntaxNode { var bookmark = new SyntaxAnnotation(BookmarkAnnotationName); - var root = await syntaxNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? root = await syntaxNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); root = root.ReplaceNode(syntaxNode, syntaxNode.WithAdditionalAnnotations(bookmark)); document = document.WithSyntaxRoot(root); root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -304,12 +301,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static NameSyntax QualifyName(IReadOnlyList qualifiers, SimpleNameSyntax simpleName) { - if (qualifiers == null) + if (qualifiers is null) { throw new ArgumentNullException(nameof(qualifiers)); } - if (simpleName == null) + if (simpleName is null) { throw new ArgumentNullException(nameof(simpleName)); } @@ -322,7 +319,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers NameSyntax result = SyntaxFactory.IdentifierName(qualifiers[0]); for (int i = 1; i < qualifiers.Count; i++) { - var rightSide = SyntaxFactory.IdentifierName(qualifiers[i]); + IdentifierNameSyntax? rightSide = SyntaxFactory.IdentifierName(qualifiers[i]); result = SyntaxFactory.QualifiedName(result, rightSide); } @@ -332,14 +329,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static CSharpSyntaxNode UpdateStatementsForAsyncMethod(CSharpSyntaxNode body, SemanticModel semanticModel, bool hasResultValue, CancellationToken cancellationToken) { var blockBody = body as BlockSyntax; - if (blockBody != null) + if (blockBody is object) { bool returnTypeChanged = false; // probably not right, but we don't have a failing test yet. return UpdateStatementsForAsyncMethod(blockBody, semanticModel, hasResultValue, returnTypeChanged, cancellationToken); } var expressionBody = body as ExpressionSyntax; - if (expressionBody != null) + if (expressionBody is object) { return SyntaxFactory.AwaitExpression(expressionBody).TrySimplify(expressionBody, semanticModel, cancellationToken); } @@ -349,7 +346,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static BlockSyntax UpdateStatementsForAsyncMethod(BlockSyntax body, SemanticModel semanticModel, bool hasResultValue, bool returnTypeChanged, CancellationToken cancellationToken) { - var fixedUpBlock = body.ReplaceNodes( + BlockSyntax? fixedUpBlock = body.ReplaceNodes( body.DescendantNodes().OfType(), (f, n) => { @@ -376,23 +373,23 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static ExpressionSyntax TrySimplify(this AwaitExpressionSyntax awaitExpression, ExpressionSyntax originalSyntax, SemanticModel semanticModel, CancellationToken cancellationToken) { - if (awaitExpression == null) + if (awaitExpression is null) { throw new ArgumentNullException(nameof(awaitExpression)); } // await Task.FromResult(x) => x. - if (semanticModel != null) + if (semanticModel is object) { if (awaitExpression.Expression is InvocationExpressionSyntax awaitedInvocation && awaitedInvocation.Expression is MemberAccessExpressionSyntax awaitedInvocationMemberAccess && awaitedInvocationMemberAccess.Name.Identifier.Text == nameof(Task.FromResult)) { // Is the FromResult method on the Task or Task class? - var memberOwnerSymbol = semanticModel.GetSymbolInfo(originalSyntax, cancellationToken).Symbol; + ISymbol? memberOwnerSymbol = semanticModel.GetSymbolInfo(originalSyntax, cancellationToken).Symbol; if (Utils.IsTask(memberOwnerSymbol?.ContainingType)) { - var simplified = awaitedInvocation.ArgumentList.Arguments.Single().Expression; + ExpressionSyntax? simplified = awaitedInvocation.ArgumentList.Arguments.Single().Expression; return simplified; } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj index eddb9851..cbaa6102 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj @@ -1,9 +1,6 @@  netstandard1.3 - true - $(NoWarn);CS1591 - ..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset Microsoft.VisualStudio.Threading.Analyzers Static code analyzer to detect common mistakes or potential issues regarding threading and async coding. diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/NullableHelpers.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/NullableHelpers.cs index 764d59d4..77bcc51a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/NullableHelpers.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/NullableHelpers.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/SyntaxGeneratorExtensions.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/SyntaxGeneratorExtensions.cs index c661b31a..7affd363 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/SyntaxGeneratorExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/SyntaxGeneratorExtensions.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -19,7 +20,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var qualifiedNameSyntaxKind = generator.QualifiedName(generator.IdentifierName("ignored"), generator.IdentifierName("ignored")).RawKind; var memberAccessExpressionSyntaxKind = generator.MemberAccessExpression(generator.IdentifierName("ignored"), "ignored").RawKind; - var typeExpression = generator.TypeExpression(typeSymbol); + SyntaxNode? typeExpression = generator.TypeExpression(typeSymbol); return QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, typeExpression, generator); // Local function @@ -27,8 +28,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { if (expression.RawKind == qualifiedNameSyntaxKind) { - var left = QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, expression.ChildNodes().First(), generator); - var right = expression.ChildNodes().Last(); + SyntaxNode? left = QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, expression.ChildNodes().First(), generator); + SyntaxNode? right = expression.ChildNodes().Last(); return generator.MemberAccessExpression(left, right); } @@ -43,7 +44,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return null; } - var declarationKind = generator.GetDeclarationKind(node); + DeclarationKind declarationKind = generator.GetDeclarationKind(node); while ((kind.HasValue && declarationKind != kind) || (!kind.HasValue && declarationKind == DeclarationKind.None)) { node = generator.GetDeclaration(node.Parent); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD002UseJtfRunCodeFixWithAwait.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD002UseJtfRunCodeFixWithAwait.cs index e992acba..bda2eac9 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD002UseJtfRunCodeFixWithAwait.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD002UseJtfRunCodeFixWithAwait.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -32,9 +29,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); + Diagnostic? diagnostic = context.Diagnostics.First(); - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (TryFindNodeAtSource(diagnostic, root, out _, out _)) { @@ -43,16 +40,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers Strings.VSTHRD002_CodeFix_Await_Title, async ct => { - var document = context.Document; - if (TryFindNodeAtSource(diagnostic, root, out var node, out var transform)) + Document? document = context.Document; + if (TryFindNodeAtSource(diagnostic, root, out ExpressionSyntax? node, out Func? transform)) { (document, node, _) = await FixUtils.UpdateDocumentAsync( document, node, n => SyntaxFactory.AwaitExpression(transform(n, ct)), ct).ConfigureAwait(false); - var method = node.FirstAncestorOrSelf(); - if (method != null) + MethodDeclarationSyntax? method = node.FirstAncestorOrSelf(); + if (method is object) { (document, method) = await FixUtils.MakeMethodAsync(method, document, ct).ConfigureAwait(false); } @@ -74,15 +71,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers target = null; var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan); - if (syntaxNode.FirstAncestorOrSelf() != null) + if (syntaxNode.FirstAncestorOrSelf() is object) { // We don't support converting anonymous delegates to async. return false; } - SimpleNameSyntax? FindStaticWaitInvocation(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken)) + SimpleNameSyntax? FindStaticWaitInvocation(ExpressionSyntax from) { - var name = ((from as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax)?.Name; + SimpleNameSyntax? name = ((from as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax)?.Name; return name?.Identifier.ValueText switch { nameof(Task.WaitAny) => name, @@ -93,7 +90,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers ExpressionSyntax? TransformStaticWhatInvocation(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken)) { - var name = FindStaticWaitInvocation(from); + SimpleNameSyntax? name = FindStaticWaitInvocation(from); var newIdentifier = name!.Identifier.ValueText switch { nameof(Task.WaitAny) => nameof(Task.WhenAny), @@ -111,27 +108,27 @@ namespace Microsoft.VisualStudio.Threading.Analyzers ExpressionSyntax? FindParentMemberAccess(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken)) => (from as MemberAccessExpressionSyntax)?.Expression; - var parentInvocation = syntaxNode.FirstAncestorOrSelf(); - var parentMemberAccess = syntaxNode.FirstAncestorOrSelf(); - if (FindTwoLevelDeepIdentifierInvocation(parentInvocation) != null) + InvocationExpressionSyntax? parentInvocation = syntaxNode.FirstAncestorOrSelf(); + MemberAccessExpressionSyntax? parentMemberAccess = syntaxNode.FirstAncestorOrSelf(); + if (FindTwoLevelDeepIdentifierInvocation(parentInvocation) is object) { // This method will not return null for the provided 'target' argument transform = NullableHelpers.AsNonNullReturnUnchecked(FindTwoLevelDeepIdentifierInvocation); target = parentInvocation; } - else if (FindStaticWaitInvocation(parentInvocation) != null) + else if (FindStaticWaitInvocation(parentInvocation) is object) { // This method will not return null for the provided 'target' argument transform = NullableHelpers.AsNonNullReturnUnchecked(TransformStaticWhatInvocation); target = parentInvocation; } - else if (FindOneLevelDeepIdentifierInvocation(parentInvocation) != null) + else if (FindOneLevelDeepIdentifierInvocation(parentInvocation) is object) { // This method will not return null for the provided 'target' argument transform = NullableHelpers.AsNonNullReturnUnchecked(FindOneLevelDeepIdentifierInvocation); target = parentInvocation; } - else if (FindParentMemberAccess(parentMemberAccess) != null) + else if (FindParentMemberAccess(parentMemberAccess) is object) { // This method will not return null for the provided 'target' argument transform = NullableHelpers.AsNonNullReturnUnchecked(FindParentMemberAccess); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD010MainThreadUsageCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD010MainThreadUsageCodeFix.cs index 81bfff90..1196c20b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD010MainThreadUsageCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD010MainThreadUsageCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -30,20 +33,20 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); + Diagnostic? diagnostic = context.Diagnostics.First(); - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); - var container = CSharpUtils.GetContainingFunction(syntaxNode); - if (container.BlockOrExpression == null) + CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode); + if (container.BlockOrExpression is null) { return; } - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - var enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); - if (enclosingSymbol == null) + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); + if (enclosingSymbol is null) { return; } @@ -70,10 +73,10 @@ { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. - var proposedMethod = Utils.FindMethodGroup(semanticModel, option) + IMethodSymbol? proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || - (cancellationTokenSymbol.Value != null && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); - if (proposedMethod == null) + (cancellationTokenSymbol.Value is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); + if (proposedMethod is null) { // We can't find it, so don't offer to use it. continue; @@ -85,7 +88,7 @@ } else { - foreach (var candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) + foreach (Tuple? candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { @@ -100,21 +103,21 @@ void OfferFix(string fullyQualifiedMethod) { - context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol, ct), fullyQualifiedMethod), context.Diagnostics); + context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol), fullyQualifiedMethod), context.Diagnostics); } } } - Task Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy cancellationTokenSymbol, CancellationToken cancellationToken) + Task Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy cancellationTokenSymbol) { int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName); - var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); - var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); - if (cancellationTokenParameter != null && cancellationTokenSymbol.Value != null) + InvocationExpressionSyntax? invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); + IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); + if (cancellationTokenParameter is object && cancellationTokenSymbol.Value is object) { - var arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name)); + ArgumentSyntax? arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); @@ -124,16 +127,16 @@ } ExpressionSyntax? awaitExpression = container.IsAsync ? SyntaxFactory.AwaitExpression(invocationExpression) : null; - var addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression) + ExpressionStatementSyntax? addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var initialBlockSyntax = container.BlockOrExpression as BlockSyntax; - if (initialBlockSyntax == null) + if (initialBlockSyntax is null) { initialBlockSyntax = SyntaxFactory.Block(SyntaxFactory.ReturnStatement((ExpressionSyntax)container.BlockOrExpression)) .WithAdditionalAnnotations(Formatter.Annotation); } - var newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement)); + BlockSyntax? newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement)); return Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(container.BlockOrExpression, newBlock))); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD100AsyncVoidMethodCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD100AsyncVoidMethodCodeFix.cs index a4ed0277..07b04f8a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD100AsyncVoidMethodCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD100AsyncVoidMethodCodeFix.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -52,7 +49,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// public override Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); + Diagnostic? diagnostic = context.Diagnostics.First(); context.RegisterCodeFix(new VoidToTaskCodeAction(context.Document, diagnostic), diagnostic); return Task.FromResult(null); } @@ -79,14 +76,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) { - var root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var methodDeclaration = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); - var taskType = SyntaxFactory.ParseTypeName(typeof(Task).FullName) + SyntaxNode? root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + MethodDeclarationSyntax? methodDeclaration = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); + TypeSyntax? taskType = SyntaxFactory.ParseTypeName(typeof(Task).FullName) .WithAdditionalAnnotations(Simplifier.Annotation) .WithTrailingTrivia(methodDeclaration.ReturnType.GetTrailingTrivia()); - var newMethodDeclaration = methodDeclaration.WithReturnType(taskType); - var newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration); - var newDocument = this.document.WithSyntaxRoot(newRoot); + MethodDeclarationSyntax? newMethodDeclaration = methodDeclaration.WithReturnType(taskType); + SyntaxNode? newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration); + Document? newDocument = this.document.WithSyntaxRoot(newRoot); return newDocument; } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD103UseAsyncOptionCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD103UseAsyncOptionCodeFix.cs index 4316df1d..8369b4e0 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD103UseAsyncOptionCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD103UseAsyncOptionCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -40,8 +43,8 @@ /// public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.FirstOrDefault(d => d.Properties.ContainsKey(VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName)); - if (diagnostic != null) + Diagnostic? diagnostic = context.Diagnostics.FirstOrDefault(d => d.Properties.ContainsKey(VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName)); + if (diagnostic is object) { // Check that the method we're replacing the sync blocking call with actually exists. // This is particularly useful when the method is an extension method, since the using directive @@ -53,8 +56,8 @@ asyncMethodName = "GetAwaiter"; } - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var blockingIdentifier = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as IdentifierNameSyntax; var memberAccessExpression = blockingIdentifier?.Parent as MemberAccessExpressionSyntax; @@ -64,8 +67,8 @@ if (!asyncAlternativeExists) { // If we fail to recognize the container, assume it exists since the analyzer thought it would. - var container = memberAccessExpression != null ? semanticModel.GetTypeInfo(memberAccessExpression.Expression, context.CancellationToken).ConvertedType : null; - asyncAlternativeExists = container == null || semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, name: asyncMethodName, container: container, includeReducedExtensionMethods: true).Any(); + ITypeSymbol? container = memberAccessExpression is object ? semanticModel.GetTypeInfo(memberAccessExpression.Expression, context.CancellationToken).ConvertedType : null; + asyncAlternativeExists = container is null || semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, name: asyncMethodName, container: container, includeReducedExtensionMethods: true).Any(); } if (asyncAlternativeExists) @@ -93,7 +96,7 @@ { get { - return this.AlternativeAsyncMethod != string.Empty + return !string.IsNullOrEmpty(this.AlternativeAsyncMethod) ? string.Format(CultureInfo.CurrentCulture, Strings.AwaitXInstead, this.AlternativeAsyncMethod) : Strings.UseAwaitInstead; } @@ -108,16 +111,16 @@ protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) { - var document = this.document; - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + Document? document = this.document; + SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Find the synchronously blocking call member, // and bookmark it so we can find it again after some mutations have taken place. var syncAccessBookmark = new SyntaxAnnotation(); SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan); - if (syncMethodName == null) + if (syncMethodName is null) { - var syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); + MemberAccessExpressionSyntax? syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); syncMethodName = syncMemberAccess.Name; } @@ -132,15 +135,15 @@ // and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!). // So after acquiring the semantic model, update it with the new method body. SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf(); - var originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf(); + AnonymousFunctionExpressionSyntax? originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf(); + MethodDeclarationSyntax? originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf(); - var enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken); + ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken); var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false; // Ensure that the method or anonymous delegate is using the async keyword. MethodDeclarationSyntax updatedMethod; - if (originalAnonymousMethodContainerIfApplicable != null) + if (originalAnonymousMethodContainerIfApplicable is object) { updatedMethod = originalMethodDeclaration.ReplaceNode( originalAnonymousMethodContainerIfApplicable, @@ -158,14 +161,14 @@ syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single(); } - var syncExpression = GetSynchronousExpression(syncMethodName); + ExpressionSyntax? syncExpression = GetSynchronousExpression(syncMethodName); ExpressionSyntax awaitExpression; - if (this.AlternativeAsyncMethod != string.Empty) + if (!string.IsNullOrEmpty(this.AlternativeAsyncMethod)) { // Replace the member being called and await the invocation expression. // While doing so, move leading trivia to the surrounding await expression. - var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName])); + SimpleNameSyntax? asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName])); awaitExpression = SyntaxFactory.AwaitExpression( syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia()) .WithLeadingTrivia(syncExpression.GetLeadingTrivia()); @@ -173,8 +176,8 @@ else { // Remove the member being accessed that causes a synchronous block and simply await the object. - var syncMemberAccess = syncMethodName.FirstAncestorOrSelf(); - var syncMemberStrippedExpression = syncMemberAccess.Expression; + MemberAccessExpressionSyntax? syncMemberAccess = syncMethodName.FirstAncestorOrSelf(); + ExpressionSyntax? syncMemberStrippedExpression = syncMemberAccess.Expression; // Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls. var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax; @@ -196,8 +199,8 @@ updatedMethod = updatedMethod .ReplaceNode(syncExpression, awaitExpression); - var newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod); - var newDocument = document.WithSyntaxRoot(newRoot); + SyntaxNode? newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod); + Document? newDocument = document.WithSyntaxRoot(newRoot); return newDocument.Project.Solution; } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD107AwaitTaskWithinUsingExpressionCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD107AwaitTaskWithinUsingExpressionCodeFix.cs index b47cf21b..c143b1a2 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD107AwaitTaskWithinUsingExpressionCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD107AwaitTaskWithinUsingExpressionCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -44,16 +47,16 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); + Diagnostic? diagnostic = context.Diagnostics.First(); context.RegisterCodeFix( CodeAction.Create( Strings.VSTHRD107_CodeFix_Title, async ct => { - var document = context.Document; - var root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false); - var method = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); + Document? document = context.Document; + SyntaxNode? root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false); + MethodDeclarationSyntax? method = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); (document, method, _) = await FixUtils.UpdateDocumentAsync( document, @@ -61,10 +64,10 @@ m => { root = m.SyntaxTree.GetRoot(ct); - var usingStatement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); - var awaitExpression = SyntaxFactory.AwaitExpression( + UsingStatementSyntax usingStatement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); + AwaitExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression( SyntaxFactory.ParenthesizedExpression(usingStatement.Expression)); - var modifiedUsingStatement = usingStatement.WithExpression(awaitExpression) + UsingStatementSyntax modifiedUsingStatement = usingStatement.WithExpression(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation); return m.ReplaceNode(usingStatement, modifiedUsingStatement); }, diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD109AvoidAssertInAsyncMethodsCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD109AvoidAssertInAsyncMethodsCodeFix.cs index 3fa3e07f..662c3dd1 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD109AvoidAssertInAsyncMethodsCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD109AvoidAssertInAsyncMethodsCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -30,17 +33,17 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - foreach (var diagnostic in context.Diagnostics) + foreach (Diagnostic? diagnostic in context.Diagnostics) { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); ExpressionSyntax? syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) as ExpressionSyntax; if (syntaxNode is null) { continue; } - var container = CSharpUtils.GetContainingFunction(syntaxNode); - if (container.BlockOrExpression == null) + CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode); + if (container.BlockOrExpression is null) { return; } @@ -54,25 +57,25 @@ } } - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - var enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); - if (enclosingSymbol == null) + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); + if (enclosingSymbol is null) { return; } var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false; - var options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken); + ImmutableArray options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken); int positionForLookup = diagnostic.Location.SourceSpan.Start; ISymbol cancellationTokenSymbol = Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault(); - foreach (var option in options) + foreach (CommonInterest.QualifiedMember option in options) { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. - var proposedMethod = Utils.FindMethodGroup(semanticModel, option) + IMethodSymbol? proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || - (cancellationTokenSymbol != null && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); - if (proposedMethod == null) + (cancellationTokenSymbol is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); + if (proposedMethod is null) { // We can't find it, so don't offer to use it. continue; @@ -84,7 +87,7 @@ } else { - foreach (var candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) + foreach (Tuple? candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { @@ -105,17 +108,17 @@ async Task Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, bool hasReturnValue, CancellationToken cancellationToken) { - var assertionStatementToRemove = syntaxNode!.FirstAncestorOrSelf(); + StatementSyntax? assertionStatementToRemove = syntaxNode!.FirstAncestorOrSelf(); int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName) .WithAdditionalAnnotations(Simplifier.Annotation); - var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); - var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); - if (cancellationTokenParameter != null && cancellationTokenSymbol != null) + InvocationExpressionSyntax? invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); + IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); + if (cancellationTokenParameter is object && cancellationTokenSymbol is object) { - var arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name)); + ArgumentSyntax? arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); @@ -125,14 +128,14 @@ } ExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression(invocationExpression); - var addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression) + ExpressionStatementSyntax? addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var methodAnnotation = new SyntaxAnnotation(); CSharpSyntaxNode methodSyntax = container.Function.ReplaceNode(assertionStatementToRemove, addedStatement) .WithAdditionalAnnotations(methodAnnotation); Document newDocument = context.Document.WithSyntaxRoot(root.ReplaceNode(container.Function, methodSyntax)); - var newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken); + SyntaxNode? newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken); methodSyntax = (CSharpSyntaxNode)newSyntaxRoot.GetAnnotatedNodes(methodAnnotation).Single(); if (!container.IsAsync) { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD111UseConfigureAwaitCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD111UseConfigureAwaitCodeFix.cs index d15adde0..785c0ddc 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD111UseConfigureAwaitCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD111UseConfigureAwaitCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; @@ -30,13 +33,13 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - foreach (var diagnostic in context.Diagnostics) + foreach (Diagnostic? diagnostic in context.Diagnostics) { - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var awaitedExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as ExpressionSyntax; - Task ApplyFix(bool captureContext, CancellationToken cancellationToken) + Task ApplyFix(bool captureContext) { ExpressionSyntax configuredAwaitExpression = SyntaxFactory.ParenthesizedExpression( SyntaxFactory.InvocationExpression( @@ -50,8 +53,8 @@ return Task.FromResult(context.Document.WithSyntaxRoot(syntaxRoot.ReplaceNode(awaitedExpression, configuredAwaitExpression))); } - context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_True_Title, ct => ApplyFix(true, ct), true.ToString()), diagnostic); - context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_False_Title, ct => ApplyFix(false, ct), false.ToString()), diagnostic); + context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_True_Title, ct => ApplyFix(true), true.ToString()), diagnostic); + context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_False_Title, ct => ApplyFix(false), false.ToString()), diagnostic); } } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD112ImplementSystemIAsyncDisposableCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD112ImplementSystemIAsyncDisposableCodeFix.cs index 8a955bbd..6762b7c6 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD112ImplementSystemIAsyncDisposableCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD112ImplementSystemIAsyncDisposableCodeFix.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -23,10 +24,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - foreach (var diagnostic in context.Diagnostics) + foreach (Diagnostic? diagnostic in context.Diagnostics) { - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); - var compilation = await context.Document.Project.GetCompilationAsync(context.CancellationToken); + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); + Compilation? compilation = await context.Document.Project.GetCompilationAsync(context.CancellationToken); if (compilation is null) { continue; @@ -38,9 +39,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers continue; } - var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken); var generator = SyntaxGenerator.GetGenerator(context.Document); - var originalTypeDeclaration = generator.TryGetContainingDeclaration(syntaxRoot.FindNode(diagnostic.Location.SourceSpan)); + SyntaxNode? originalTypeDeclaration = generator.TryGetContainingDeclaration(syntaxRoot.FindNode(diagnostic.Location.SourceSpan)); if (originalTypeDeclaration is null) { continue; @@ -52,13 +53,21 @@ namespace Microsoft.VisualStudio.Threading.Analyzers ct => { // Declare that the type implements the System.IAsyncDisposable interface. - var newBaseType = generator.TypeExpression(bclAsyncDisposableType); - var typeDeclaration = generator.AddInterfaceType(originalTypeDeclaration, newBaseType); + SyntaxNode? newBaseType = generator.TypeExpression(bclAsyncDisposableType); + SyntaxNode? typeDeclaration = generator.AddInterfaceType(originalTypeDeclaration, newBaseType); // Implement the interface, if we're on a non-interface type. if (semanticModel.GetDeclaredSymbol(originalTypeDeclaration, ct) is ITypeSymbol changedSymbol && changedSymbol.TypeKind != TypeKind.Interface) { var disposeAsyncMethod = (IMethodSymbol)bclAsyncDisposableType.GetMembers().Single(); + var statements = new SyntaxNode[] + { + generator.ReturnStatement( + generator.ObjectCreationExpression( + disposeAsyncMethod.ReturnType, + generator.InvocationExpression( + generator.MemberAccessExpression(generator.ThisExpression(), "DisposeAsync")))), + }; typeDeclaration = generator.AddMembers( typeDeclaration, generator.AsPrivateInterfaceImplementation( @@ -66,14 +75,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers disposeAsyncMethod.Name, returnType: generator.TypeExpression(disposeAsyncMethod.ReturnType), accessibility: Accessibility.Public, - statements: new SyntaxNode[] - { - generator.ReturnStatement( - generator.ObjectCreationExpression( - disposeAsyncMethod.ReturnType, - generator.InvocationExpression( - generator.MemberAccessExpression(generator.ThisExpression(), "DisposeAsync")))), - }), + statements: statements), generator.TypeExpression(bclAsyncDisposableType))); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD114AvoidReturningNullTaskCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD114AvoidReturningNullTaskCodeFix.cs index b5cd70b9..a9dc83fa 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD114AvoidReturningNullTaskCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD114AvoidReturningNullTaskCodeFix.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System.Collections.Immutable; using System.Threading; @@ -23,27 +26,27 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - foreach (var diagnostic in context.Diagnostics) + foreach (Diagnostic? diagnostic in context.Diagnostics) { - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - var nullLiteral = syntaxRoot.FindNode(diagnostic.Location.SourceSpan); + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode? nullLiteral = syntaxRoot.FindNode(diagnostic.Location.SourceSpan); if (semanticModel.GetOperation(nullLiteral, context.CancellationToken) is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) { - var typeInfo = semanticModel.GetTypeInfo(nullLiteral, context.CancellationToken); + TypeInfo typeInfo = semanticModel.GetTypeInfo(nullLiteral, context.CancellationToken); if (typeInfo.ConvertedType is INamedTypeSymbol returnType) { if (returnType.IsGenericType) { - context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_FromResult, ct => ApplyTaskFromResultFix(returnType, ct), nameof(Task.FromResult)), diagnostic); + context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_FromResult, ct => ApplyTaskFromResultFix(returnType), nameof(Task.FromResult)), diagnostic); } else { - context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_CompletedTask, ct => ApplyTaskCompletedTaskFix(returnType, ct), nameof(Task.CompletedTask)), diagnostic); + context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_CompletedTask, ct => ApplyTaskCompletedTaskFix(returnType), nameof(Task.CompletedTask)), diagnostic); } } - Task ApplyTaskCompletedTaskFix(INamedTypeSymbol returnType, CancellationToken cancellationToken) + Task ApplyTaskCompletedTaskFix(INamedTypeSymbol returnType) { var generator = SyntaxGenerator.GetGenerator(context.Document); SyntaxNode completedTaskExpression = generator.MemberAccessExpression( @@ -53,7 +56,7 @@ return Task.FromResult(context.Document.WithSyntaxRoot(syntaxRoot.ReplaceNode(nullLiteral, completedTaskExpression))); } - Task ApplyTaskFromResultFix(INamedTypeSymbol returnType, CancellationToken cancellationToken) + Task ApplyTaskFromResultFix(INamedTypeSymbol returnType) { var generator = SyntaxGenerator.GetGenerator(context.Document); SyntaxNode taskFromResultExpression = generator.InvocationExpression( diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD200UseAsyncNamingConventionCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD200UseAsyncNamingConventionCodeFix.cs index ee58b427..39fe62fa 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD200UseAsyncNamingConventionCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/VSTHRD200UseAsyncNamingConventionCodeFix.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -33,7 +30,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// public override Task RegisterCodeFixesAsync(CodeFixContext context) { - var diagnostic = context.Diagnostics.First(); + Diagnostic? diagnostic = context.Diagnostics.First(); context.RegisterCodeFix(new AddAsyncSuffixCodeAction(context.Document, diagnostic), diagnostic); return Task.FromResult(null); } @@ -64,14 +61,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) { - var root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + SyntaxNode? root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var methodDeclaration = (MethodDeclarationSyntax)root.FindNode(this.diagnostic.Location.SourceSpan); - var semanticModel = await this.document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken); + SemanticModel? semanticModel = await this.document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken); - var solution = this.document.Project.Solution; - var updatedSolution = await Renamer.RenameSymbolAsync( + Solution? solution = this.document.Project.Solution; + Solution? updatedSolution = await Renamer.RenameSymbolAsync( solution, methodSymbol, this.NewName, diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.ruleset b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.ruleset deleted file mode 100644 index 47d2a34f..00000000 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.ruleset +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 33d700fe..00000000 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/AssemblyInfo.cs index 17743930..18c0aa28 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/AssemblyInfo.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Resources; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj index 93088d9f..eec64de0 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj @@ -1,9 +1,6 @@  netstandard1.3 - true - $(NoWarn);CS1591 - ..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset Microsoft.VisualStudio.Threading.Analyzers false diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicUtils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicUtils.cs index 97dfc90d..c08d9ead 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicUtils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicUtils.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -18,16 +19,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal override Location? GetLocationOfBaseTypeName(INamedTypeSymbol symbol, INamedTypeSymbol baseType, Compilation compilation, CancellationToken cancellationToken) { - foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) + foreach (SyntaxReference? syntaxReference in symbol.DeclaringSyntaxReferences) { - var syntaxNode = syntaxReference.GetSyntax(cancellationToken); + SyntaxNode? syntaxNode = syntaxReference.GetSyntax(cancellationToken); if (syntaxNode is InterfaceStatementSyntax { Parent: InterfaceBlockSyntax vbInterface }) { if (compilation.GetSemanticModel(vbInterface.SyntaxTree) is { } semanticModel) { - foreach (var inheritStatement in vbInterface.Inherits) + foreach (InheritsStatementSyntax? inheritStatement in vbInterface.Inherits) { - foreach (var typeSyntax in inheritStatement.Types) + foreach (TypeSyntax? typeSyntax in inheritStatement.Types) { SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken); if (Equals(baseTypeSymbolInfo.Symbol, baseType)) @@ -42,9 +43,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { if (compilation.GetSemanticModel(vbClass.SyntaxTree) is { } semanticModel) { - foreach (var implementStatement in vbClass.Implements) + foreach (ImplementsStatementSyntax? implementStatement in vbClass.Implements) { - foreach (var typeSyntax in implementStatement.Types) + foreach (TypeSyntax? typeSyntax in implementStatement.Types) { SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken); if (Equals(baseTypeSymbolInfo.Symbol, baseType)) @@ -59,9 +60,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { if (compilation.GetSemanticModel(vbStruct.SyntaxTree) is { } semanticModel) { - foreach (var implementStatement in vbStruct.Implements) + foreach (ImplementsStatementSyntax? implementStatement in vbStruct.Implements) { - foreach (var typeSyntax in implementStatement.Types) + foreach (TypeSyntax? typeSyntax in implementStatement.Types) { SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken); if (Equals(baseTypeSymbolInfo.Symbol, baseType)) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs index c0cf41c8..b2401176 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs index a294b778..cd7ef37c 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs index 0f570c28..1ab6ea85 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD011UseAsyncLazyAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs index b3e6e140..61e7fcde 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD012SpecifyJtfWhereAllowed.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs index 9a249c46..ff5d0851 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs index 8c9cc2be..5b6c25c2 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD108AssertThreadRequirementUnconditionally.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs index 211bdf91..0fe805f7 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs index be2bee1f..950ef4b1 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.VisualBasic/VisualBasicVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs index 11081d08..0cc75327 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD001UseSwitchToMainThreadAsyncAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -51,10 +52,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal void AnalyzeInvocation(OperationAnalysisContext context) { var invocation = (IInvocationOperation)context.Operation; - var invokeMethod = invocation.TargetMethod; - if (invokeMethod != null) + IMethodSymbol? invokeMethod = invocation.TargetMethod; + if (invokeMethod is object) { - foreach (var legacyMethod in this.legacyThreadSwitchingMembers) + foreach (CommonInterest.QualifiedMember legacyMethod in this.legacyThreadSwitchingMembers) { context.CancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs index 0ae13e69..85c58bed 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD004AwaitSwitchToMainThreadAsyncAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -43,7 +40,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeInvocation(OperationAnalysisContext context) { var invocation = (IInvocationOperation)context.Operation; - var methodSymbol = invocation.TargetMethod; + IMethodSymbol? methodSymbol = invocation.TargetMethod; if (methodSymbol.Name == Types.JoinableTaskFactory.SwitchToMainThreadAsync && methodSymbol.ContainingType.Name == Types.JoinableTaskFactory.TypeName && methodSymbol.ContainingType.BelongsToNamespace(Types.JoinableTaskFactory.Namespace)) @@ -51,7 +48,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers // This is a call to JTF.SwitchToMainThreadAsync(). Is it being (directly) awaited? if (!(invocation.Parent is IAwaitOperation)) { - var location = (this.LanguageUtils.IsolateMethodName(invocation) ?? invocation.Syntax).GetLocation(); + Location? location = (this.LanguageUtils.IsolateMethodName(invocation) ?? invocation.Syntax).GetLocation(); context.ReportDiagnostic(Diagnostic.Create(Descriptor, location)); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs index a814587c..adf01b2c 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -53,11 +54,11 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeNode(OperationAnalysisContext context) { var objectCreation = (IObjectCreationOperation)context.Operation; - var methodSymbol = objectCreation.Constructor; + IMethodSymbol? methodSymbol = objectCreation.Constructor; var constructedType = methodSymbol?.ReceiverType as INamedTypeSymbol; if (Utils.IsLazyOfT(constructedType)) { - var typeArg = constructedType.TypeArguments.FirstOrDefault(); + ITypeSymbol? typeArg = constructedType.TypeArguments.FirstOrDefault(); bool typeArgIsTask = typeArg?.Name == nameof(Task) && typeArg.BelongsToNamespace(Namespaces.SystemThreadingTasks); if (typeArgIsTask) @@ -66,15 +67,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers } else { - var firstArgExpression = objectCreation.Arguments.FirstOrDefault()?.Value; + IOperation? firstArgExpression = objectCreation.Arguments.FirstOrDefault()?.Value; if (firstArgExpression is IDelegateCreationOperation { Target: IAnonymousFunctionOperation anonFunc }) { - var problems = from invocation in anonFunc.Descendants().OfType() + System.Collections.Generic.IEnumerable? problems = from invocation in anonFunc.Descendants().OfType() let invokedSymbol = invocation.TargetMethod - where invokedSymbol != null && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol)) + where invokedSymbol is object && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol)) select invocation; - var firstProblem = problems.FirstOrDefault(); - if (firstProblem != null) + IInvocationOperation? firstProblem = problems.FirstOrDefault(); + if (firstProblem is object) { context.ReportDiagnostic(Diagnostic.Create(SyncBlockInValueFactoryDescriptor, this.LanguageUtils.IsolateMethodName(firstProblem).GetLocation())); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD012SpecifyJtfWhereAllowed.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD012SpecifyJtfWhereAllowed.cs index 8b79c2ff..3ad070f1 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD012SpecifyJtfWhereAllowed.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD012SpecifyJtfWhereAllowed.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -46,7 +47,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static IArgumentOperation? GetArgumentForParameter(ImmutableArray arguments, IParameterSymbol parameter) { - foreach (var argument in arguments) + foreach (IArgumentOperation? argument in arguments) { if (Equals(argument.Parameter, parameter)) { @@ -59,14 +60,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static void AnalyzeCall(OperationAnalysisContext context, Location location, ImmutableArray argList, IMethodSymbol methodSymbol, IEnumerable otherOverloads) { - var firstJtfParameter = methodSymbol.Parameters.FirstOrDefault(IsImportantJtfParameter); - if (firstJtfParameter != null) + IParameterSymbol? firstJtfParameter = methodSymbol.Parameters.FirstOrDefault(IsImportantJtfParameter); + if (firstJtfParameter is object) { // Verify that if the JTF/JTC parameter is optional, it is actually specified in the caller's syntax. if (firstJtfParameter.HasExplicitDefaultValue) { - var argument = GetArgumentForParameter(argList, firstJtfParameter); - if (argument == null || argument.IsImplicit) + IArgumentOperation? argument = GetArgumentForParameter(argList, firstJtfParameter); + if (argument is null || argument.IsImplicit) { Diagnostic diagnostic = Diagnostic.Create( Descriptor, @@ -95,18 +96,18 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeInvocation(OperationAnalysisContext context) { var invocation = (IInvocationOperation)context.Operation; - var invokedMethodName = this.LanguageUtils.IsolateMethodName(invocation); - var argList = invocation.Arguments; - var methodSymbol = invocation.TargetMethod; + SyntaxNode? invokedMethodName = this.LanguageUtils.IsolateMethodName(invocation); + ImmutableArray argList = invocation.Arguments; + IMethodSymbol? methodSymbol = invocation.TargetMethod; - var otherOverloads = methodSymbol.ContainingType.GetMembers(methodSymbol.Name).OfType(); + IEnumerable? otherOverloads = methodSymbol.ContainingType.GetMembers(methodSymbol.Name).OfType(); AnalyzeCall(context, invokedMethodName.GetLocation(), argList, methodSymbol, otherOverloads); } private void AnalyzerObjectCreation(OperationAnalysisContext context) { var objectCreation = (IObjectCreationOperation)context.Operation; - var methodSymbol = objectCreation.Constructor; + IMethodSymbol? methodSymbol = objectCreation.Constructor; AnalyzeCall( context, this.LanguageUtils.IsolateMethodName(objectCreation).GetLocation(), diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs index 9639efa7..c8e0fe35 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -51,7 +52,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeInvocation(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; - var invokeMethod = operation.TargetMethod; + IMethodSymbol? invokeMethod = operation.TargetMethod; if (invokeMethod?.ContainingType.BelongsToNamespace(Namespaces.SystemThreadingTasks) ?? false) { bool reportDiagnostic = false; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD108AssertThreadRequirementUnconditionally.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD108AssertThreadRequirementUnconditionally.cs index cc66c4db..30d41c47 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD108AssertThreadRequirementUnconditionally.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD108AssertThreadRequirementUnconditionally.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -64,7 +65,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static bool IsInConditional(IOperation operation) { - foreach (var ancestor in GetAncestorsWithinMethod(operation)) + foreach (IOperation? ancestor in GetAncestorsWithinMethod(operation)) { if (ancestor is IConditionalOperation || ancestor is IWhileLoopOperation { ConditionIsTop: true } || @@ -92,11 +93,11 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static bool IsArgInInvocationToConditionalMethod(OperationAnalysisContext context) { - var argument = GetAncestorsWithinMethod(context.Operation).OfType().FirstOrDefault(); + IArgumentOperation? argument = GetAncestorsWithinMethod(context.Operation).OfType().FirstOrDefault(); var containingInvocation = argument?.Parent as IInvocationOperation; - if (containingInvocation != null) + if (containingInvocation is object) { - var symbolOfContainingMethodInvocation = containingInvocation.TargetMethod; + IMethodSymbol? symbolOfContainingMethodInvocation = containingInvocation.TargetMethod; return symbolOfContainingMethodInvocation?.GetAttributes().Any(a => a.AttributeClass.BelongsToNamespace(Namespaces.SystemDiagnostics) && a.AttributeClass.Name == nameof(System.Diagnostics.ConditionalAttribute)) ?? false; @@ -108,8 +109,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeInvocation(OperationAnalysisContext context, ImmutableArray mainThreadAssertingMethods) { var invocation = (IInvocationOperation)context.Operation; - var symbol = invocation.TargetMethod; - if (symbol != null) + IMethodSymbol? symbol = invocation.TargetMethod; + if (symbol is object) { bool reportDiagnostic = false; if (mainThreadAssertingMethods.Contains(symbol)) @@ -129,7 +130,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers if (reportDiagnostic) { - var nodeToLocate = this.LanguageUtils.IsolateMethodName(invocation); + SyntaxNode? nodeToLocate = this.LanguageUtils.IsolateMethodName(invocation); context.ReportDiagnostic(Diagnostic.Create(Descriptor, nodeToLocate.GetLocation())); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs index 923ad173..0aae792a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD109AvoidAssertInAsyncMethodsAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System.Collections.Immutable; using Microsoft.CodeAnalysis; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs index e133e888..98507f27 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD112ImplementSystemIAsyncDisposableAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AssemblyInfo.cs index a9de5bac..bf2463b3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AssemblyInfo.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Resources; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs index 403abf87..eaa836af 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/CommonInterest.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + namespace Microsoft.VisualStudio.Threading.Analyzers { using System; @@ -92,24 +95,24 @@ namespace Microsoft.VisualStudio.Threading.Analyzers string typeName = typeNameElements[typeNameElements.Length - 1]; var containingNamespace = typeNameElements.Take(typeNameElements.Length - 1).ToImmutableArray(); var type = new QualifiedType(containingNamespace, typeName); - var member = match.Groups["memberName"].Success ? new QualifiedMember(type, match.Groups["memberName"].Value) : default(QualifiedMember); + QualifiedMember member = match.Groups["memberName"].Success ? new QualifiedMember(type, match.Groups["memberName"].Value) : default(QualifiedMember); yield return new TypeMatchSpec(type, member, inverted); } } internal static IEnumerable ReadAdditionalFiles(AnalyzerOptions analyzerOptions, Regex fileNamePattern, CancellationToken cancellationToken) { - if (analyzerOptions == null) + if (analyzerOptions is null) { throw new ArgumentNullException(nameof(analyzerOptions)); } - if (fileNamePattern == null) + if (fileNamePattern is null) { throw new ArgumentNullException(nameof(fileNamePattern)); } - var docs = from file in analyzerOptions.AdditionalFiles.OrderBy(x => x.Path, StringComparer.Ordinal) + IEnumerable? docs = from file in analyzerOptions.AdditionalFiles.OrderBy(x => x.Path, StringComparer.Ordinal) let fileName = Path.GetFileName(file.Path) where fileNamePattern.IsMatch(fileName) let text = file.GetText(cancellationToken) @@ -119,7 +122,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool Contains(this ImmutableArray methods, ISymbol symbol) { - foreach (var method in methods) + foreach (QualifiedMember method in methods) { if (method.IsMatch(symbol)) { @@ -133,7 +136,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool Contains(this ImmutableArray types, [NotNullWhen(true)] ITypeSymbol? typeSymbol, ISymbol? memberSymbol) { TypeMatchSpec matching = default(TypeMatchSpec); - foreach (var type in types) + foreach (TypeMatchSpec type in types) { if (type.IsMatch(typeSymbol, memberSymbol)) { @@ -154,7 +157,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static IEnumerable ReadLinesFromAdditionalFile(SourceText text) { - if (text == null) + if (text is null) { throw new ArgumentNullException(nameof(text)); } @@ -163,7 +166,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { string lineText = line.ToString(); - if (!string.IsNullOrWhiteSpace(lineText) && !lineText.StartsWith("#")) + if (!string.IsNullOrWhiteSpace(lineText) && !lineText.StartsWith("#", StringComparison.OrdinalIgnoreCase)) { yield return lineText; } @@ -193,7 +196,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers this.Type = type; this.Member = member; - if (this.IsWildcard && this.Member.Name != null) + if (this.IsWildcard && this.Member.Name is object) { throw new ArgumentException("Wildcard use is not allowed when member of type is specified."); } @@ -217,7 +220,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// /// Gets a value indicating whether a member match is reuqired. /// - internal bool IsMember => this.Member.Name != null; + internal bool IsMember => this.Member.Name is object; /// /// Gets a value indicating whether the typename is a wildcard. @@ -227,14 +230,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// /// Gets a value indicating whether this is an uninitialized (default) instance. /// - internal bool IsEmpty => this.Type.Namespace == null; + internal bool IsEmpty => this.Type.Namespace is null; /// /// Tests whether a given symbol matches the description of a type (independent of its property). /// internal bool IsMatch([NotNullWhen(true)] ITypeSymbol? typeSymbol, ISymbol? memberSymbol) { - if (typeSymbol == null) + if (typeSymbol is null) { return false; } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/DiagnosticAnalyzerState.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/DiagnosticAnalyzerState.cs index dd3509a5..33d26622 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/DiagnosticAnalyzerState.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/DiagnosticAnalyzerState.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -28,25 +25,25 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal bool IsAwaitableType(ITypeSymbol typeSymbol, Compilation compilation, CancellationToken cancellationToken) { - if (typeSymbol == null) + if (typeSymbol is null) { return false; } if (!this.customAwaitableTypes.TryGetValue(typeSymbol, out bool isAwaitable)) { - var getAwaiterMethod = typeSymbol.GetMembers(nameof(Task.GetAwaiter)).OfType().FirstOrDefault(m => m.Parameters.IsEmpty); - if (getAwaiterMethod != null) + IMethodSymbol? getAwaiterMethod = typeSymbol.GetMembers(nameof(Task.GetAwaiter)).OfType().FirstOrDefault(m => m.Parameters.IsEmpty); + if (getAwaiterMethod is object) { isAwaitable = ConformsToAwaiterPattern(getAwaiterMethod.ReturnType); } else { - var awaitableTypesFromThisAssembly = from candidateAwaiterMethod in compilation.GetSymbolsWithName(m => m == GetAwaiterMethodName, SymbolFilter.Member, cancellationToken).OfType() + IEnumerable? awaitableTypesFromThisAssembly = from candidateAwaiterMethod in compilation.GetSymbolsWithName(m => m == GetAwaiterMethodName, SymbolFilter.Member, cancellationToken).OfType() where candidateAwaiterMethod.IsExtensionMethod && !candidateAwaiterMethod.Parameters.IsEmpty where ConformsToAwaiterPattern(candidateAwaiterMethod.ReturnType) select candidateAwaiterMethod.Parameters[0].Type; - var awaitableTypesPerAssembly = from assembly in compilation.Assembly.Modules.First().ReferencedAssemblySymbols + IEnumerable? awaitableTypesPerAssembly = from assembly in compilation.Assembly.Modules.First().ReferencedAssemblySymbols from awaitableType in GetAwaitableTypes(assembly) select awaitableType; isAwaitable = awaitableTypesFromThisAssembly.Concat(awaitableTypesPerAssembly).Contains(typeSymbol); @@ -60,7 +57,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static bool ConformsToAwaiterPattern(ITypeSymbol typeSymbol) { - if (typeSymbol == null) + if (typeSymbol is null) { return false; } @@ -69,7 +66,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var hasOnCompletedMethod = false; var hasIsCompletedProperty = false; - foreach (var member in typeSymbol.GetMembers()) + foreach (ISymbol? member in typeSymbol.GetMembers()) { hasGetResultMethod |= member.Name == nameof(TaskAwaiter.GetResult) && member is IMethodSymbol m && m.Parameters.IsEmpty; hasOnCompletedMethod |= member.Name == nameof(TaskAwaiter.OnCompleted) && member is IMethodSymbol; @@ -86,7 +83,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static IEnumerable GetAwaitableTypes(IAssemblySymbol assembly) { - if (assembly == null) + if (assembly is null) { throw new ArgumentNullException(nameof(assembly)); } @@ -101,24 +98,24 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private static IEnumerable GetAwaitableTypes(INamespaceOrTypeSymbol namespaceOrTypeSymbol) { - if (namespaceOrTypeSymbol == null || !namespaceOrTypeSymbol.DeclaredAccessibility.HasFlag(Accessibility.Public)) + if (namespaceOrTypeSymbol is null || namespaceOrTypeSymbol.DeclaredAccessibility != Accessibility.Public) { yield break; } - foreach (var member in namespaceOrTypeSymbol.GetMembers()) + foreach (ISymbol? member in namespaceOrTypeSymbol.GetMembers()) { switch (member) { case INamespaceOrTypeSymbol nsOrType: - foreach (var nested in GetAwaitableTypes(nsOrType)) + foreach (ITypeSymbol? nested in GetAwaitableTypes(nsOrType)) { yield return nested; } break; case IMethodSymbol method: - if (method.DeclaredAccessibility.HasFlag(Accessibility.Public) && + if (method.DeclaredAccessibility == Accessibility.Public && method.IsExtensionMethod && method.Name == GetAwaiterMethodName && !method.Parameters.IsEmpty && diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/LanguageUtils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/LanguageUtils.cs index 58d80793..fe49f889 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/LanguageUtils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/LanguageUtils.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj index 2bd7ad0c..90b28bdc 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj @@ -1,10 +1,6 @@  netstandard1.3 - true - $(NoWarn);CS1591 - Microsoft.VisualStudio.Threading.Analyzers.ruleset - Microsoft.VisualStudio.Threading.Analyzers.Only false diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.ruleset b/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.ruleset deleted file mode 100644 index efbe81a8..00000000 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.ruleset +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Namespaces.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Namespaces.cs index bb6b1d78..5aa3b278 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Namespaces.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Namespaces.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs index 894b3c82..7f001718 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs index ec19d514..b09d5681 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -115,8 +112,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers for (int i = 0; i < ctxt.OperationBlocks.Length; i++) { - var operation = ctxt.OperationBlocks[i]; - var lineSpan = operation.Syntax.GetLocation()?.GetLineSpan(); + IOperation? operation = ctxt.OperationBlocks[i]; + FileLinePositionSpan? lineSpan = operation.Syntax.GetLocation()?.GetLineSpan(); if (i > 0) { @@ -154,7 +151,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool IsDerivedFrom(ITypeSymbol? type, ITypeSymbol expectedType) { type = type?.BaseType; - while (type != null) + while (type is object) { if (EqualityComparer.Default.Equals(type.OriginalDefinition, expectedType)) { @@ -215,12 +212,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// true if the symbol belongs to the given namespace; otherwise false. internal static bool BelongsToNamespace(this ISymbol symbol, IReadOnlyList namespaces) { - if (namespaces == null) + if (namespaces is null) { throw new ArgumentNullException(nameof(namespaces)); } - if (symbol == null) + if (symbol is null) { return false; } @@ -241,8 +238,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static IBlockOperation? GetContainingFunctionBlock(IOperation operation) { - var previousAncestor = operation; - var ancestor = previousAncestor; + IOperation? previousAncestor = operation; + IOperation? ancestor = previousAncestor; do { if (previousAncestor != ancestor) @@ -252,7 +249,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers ancestor = ancestor.Parent; } - while (ancestor != null && ancestor.Kind != OperationKind.MethodBodyOperation && ancestor.Kind != OperationKind.AnonymousFunction && + while (ancestor is object && ancestor.Kind != OperationKind.MethodBodyOperation && ancestor.Kind != OperationKind.AnonymousFunction && ancestor.Kind != OperationKind.LocalFunction); return previousAncestor as IBlockOperation; @@ -260,7 +257,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static ISymbol GetContainingFunction(IOperation operation, ISymbol operationBlockContainingSymbol) { - for (var current = operation; current is object; current = current.Parent) + for (IOperation? current = operation; current is object; current = current.Parent) { if (current.Kind == OperationKind.AnonymousFunction) { @@ -289,7 +286,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// internal static bool IsAsyncCompatibleReturnType([NotNullWhen(true)] this ITypeSymbol? typeSymbol) { - if (typeSymbol == null) + if (typeSymbol is null) { return false; } @@ -365,7 +362,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// internal static bool IsPublic([NotNullWhen(true)] ISymbol? symbol) { - if (symbol == null) + if (symbol is null) { return false; } @@ -392,7 +389,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers case Accessibility.Protected: case Accessibility.ProtectedOrInternal: case Accessibility.Public: - return symbol.ContainingType == null || IsPublic(symbol.ContainingType); + return symbol.ContainingType is null || IsPublic(symbol.ContainingType); case Accessibility.NotApplicable: default: return false; @@ -401,7 +398,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static bool IsEntrypointMethod([NotNullWhen(true)] ISymbol? symbol, SemanticModel semanticModel, CancellationToken cancellationToken) { - return semanticModel.Compilation != null && IsEntrypointMethod(symbol, semanticModel.Compilation, cancellationToken); + return semanticModel.Compilation is object && IsEntrypointMethod(symbol, semanticModel.Compilation, cancellationToken); } internal static bool IsEntrypointMethod([NotNullWhen(true)] ISymbol? symbol, Compilation compilation, CancellationToken cancellationToken) @@ -416,12 +413,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static IEnumerable FindInterfacesImplemented(this ISymbol? symbol) { - if (symbol == null) + if (symbol is null) { return Enumerable.Empty(); } - var interfaceImplementations = from iface in symbol.ContainingType.AllInterfaces + IEnumerable? interfaceImplementations = from iface in symbol.ContainingType.AllInterfaces from member in iface.GetMembers() let implementingMember = symbol.ContainingType.FindImplementationForInterfaceMember(member) where implementingMember?.Equals(symbol) ?? false @@ -432,20 +429,20 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static string GetFullName(ISymbol symbol) { - if (symbol == null) + if (symbol is null) { throw new ArgumentNullException(nameof(symbol)); } var sb = new StringBuilder(); sb.Append(symbol.Name); - while (symbol.ContainingType != null) + while (symbol.ContainingType is object) { sb.Insert(0, symbol.ContainingType.Name + "."); symbol = symbol.ContainingType; } - while (symbol.ContainingNamespace != null) + while (symbol.ContainingNamespace is object) { if (!string.IsNullOrEmpty(symbol.ContainingNamespace.Name)) { @@ -494,18 +491,18 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// Candidate symbols. internal static IEnumerable? FindCancellationToken(SemanticModel semanticModel, int positionForLookup, CancellationToken cancellationToken) { - if (semanticModel == null) + if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } - var enclosingSymbol = semanticModel.GetEnclosingSymbol(positionForLookup, cancellationToken); - if (enclosingSymbol == null) + ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(positionForLookup, cancellationToken); + if (enclosingSymbol is null) { return null; } - var cancellationTokenSymbols = semanticModel.LookupSymbols(positionForLookup) + IOrderedEnumerable? cancellationTokenSymbols = semanticModel.LookupSymbols(positionForLookup) .Where(s => (s.IsStatic || !enclosingSymbol.IsStatic) && s.CanBeReferencedByName && IsSymbolTheRightType(s, nameof(CancellationToken), Namespaces.SystemThreading)) .OrderBy(s => s.ContainingSymbol.Equals(enclosingSymbol) ? 1 : s.ContainingType.Equals(enclosingSymbol.ContainingType) ? 2 : 3); // prefer locality return cancellationTokenSymbols; @@ -519,7 +516,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// An enumeration of method symbols with a matching name. internal static IEnumerable FindMethodGroup(SemanticModel semanticModel, string methodAsString) { - if (semanticModel == null) + if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } @@ -529,15 +526,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers throw new ArgumentException("A non-empty value is required.", nameof(methodAsString)); } - var (fullTypeName, methodName) = SplitOffLastElement(methodAsString); - var (ns, leafTypeName) = SplitOffLastElement(fullTypeName); + (string? fullTypeName, string? methodName) = SplitOffLastElement(methodAsString); + (string? ns, string? leafTypeName) = SplitOffLastElement(fullTypeName); string[]? namespaces = ns?.Split('.'); - if (fullTypeName == null) + if (fullTypeName is null) { return Enumerable.Empty(); } - var proposedType = semanticModel.Compilation.GetTypeByMetadataName(fullTypeName); + INamedTypeSymbol? proposedType = semanticModel.Compilation.GetTypeByMetadataName(fullTypeName); return proposedType?.GetMembers(methodName).OfType() ?? Enumerable.Empty(); } @@ -550,12 +547,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// An enumeration of method symbols with a matching name. internal static IEnumerable FindMethodGroup(SemanticModel semanticModel, CommonInterest.QualifiedMember method) { - if (semanticModel == null) + if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } - var proposedType = semanticModel.Compilation.GetTypeByMetadataName(method.ContainingType.ToString()); + INamedTypeSymbol? proposedType = semanticModel.Compilation.GetTypeByMetadataName(method.ContainingType.ToString()); return proposedType?.GetMembers(method.Name).OfType() ?? Enumerable.Empty(); } @@ -570,40 +567,40 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// An enumeration of symbols that can provide a value of the required type, together with a flag indicating whether they are accessible using "local" syntax (i.e. the symbol is a local variable or a field on the enclosing type). internal static IEnumerable> FindInstanceOf(INamedTypeSymbol typeSymbol, SemanticModel semanticModel, int positionForLookup, CancellationToken cancellationToken) { - if (typeSymbol == null) + if (typeSymbol is null) { throw new ArgumentNullException(nameof(typeSymbol)); } - if (semanticModel == null) + if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } - var enclosingSymbol = semanticModel.GetEnclosingSymbol(positionForLookup, cancellationToken); + ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(positionForLookup, cancellationToken); // Search fields on the declaring type. // Consider local variables too, if they're captured in a closure from some surrounding code block // such that they would presumably be initialized by the time the first statement in our own code block runs. ITypeSymbol enclosingTypeSymbol = enclosingSymbol as ITypeSymbol ?? enclosingSymbol.ContainingType; - if (enclosingTypeSymbol != null) + if (enclosingTypeSymbol is object) { - var candidateMembers = from symbol in semanticModel.LookupSymbols(positionForLookup, enclosingTypeSymbol) + IEnumerable? candidateMembers = from symbol in semanticModel.LookupSymbols(positionForLookup, enclosingTypeSymbol) where symbol.IsStatic || !enclosingSymbol.IsStatic where IsSymbolTheRightType(symbol, typeSymbol.Name, typeSymbol.ContainingNamespace) select symbol; - foreach (var candidate in candidateMembers) + foreach (ISymbol? candidate in candidateMembers) { yield return Tuple.Create(true, candidate); } } // Find static fields/properties that return the matching type from other public, non-generic types. - var candidateStatics = from offering in semanticModel.LookupStaticMembers(positionForLookup).OfType() + IEnumerable? candidateStatics = from offering in semanticModel.LookupStaticMembers(positionForLookup).OfType() from symbol in offering.GetMembers() where symbol.IsStatic && symbol.CanBeReferencedByName && IsSymbolTheRightType(symbol, typeSymbol.Name, typeSymbol.ContainingNamespace) select symbol; - foreach (var candidate in candidateStatics) + foreach (ISymbol? candidate in candidateStatics) { yield return Tuple.Create(false, candidate); } @@ -612,13 +609,13 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static T? FirstAncestor(this SyntaxNode startingNode, IReadOnlyCollection doNotPassNodeTypes) where T : SyntaxNode { - if (doNotPassNodeTypes == null) + if (doNotPassNodeTypes is null) { throw new ArgumentNullException(nameof(doNotPassNodeTypes)); } - var syntaxNode = startingNode; - while (syntaxNode != null) + SyntaxNode? syntaxNode = startingNode; + while (syntaxNode is object) { if (syntaxNode is T result) { @@ -638,7 +635,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static Tuple SplitOffLastElement(string? qualifiedName) { - if (qualifiedName == null) + if (qualifiedName is null) { return Tuple.Create(null, null); } @@ -704,7 +701,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var propertySymbol = symbol as IPropertySymbol; var parameterSymbol = symbol as IParameterSymbol; var localSymbol = symbol as ILocalSymbol; - var memberType = fieldSymbol?.Type ?? propertySymbol?.Type ?? parameterSymbol?.Type ?? localSymbol?.Type; + ITypeSymbol? memberType = fieldSymbol?.Type ?? propertySymbol?.Type ?? parameterSymbol?.Type ?? localSymbol?.Type; return memberType?.Name == typeName && memberType.BelongsToNamespace(namespaces); } @@ -714,7 +711,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers var propertySymbol = symbol as IPropertySymbol; var parameterSymbol = symbol as IParameterSymbol; var localSymbol = symbol as ILocalSymbol; - var memberType = fieldSymbol?.Type ?? propertySymbol?.Type ?? parameterSymbol?.Type ?? localSymbol?.Type; + ITypeSymbol? memberType = fieldSymbol?.Type ?? propertySymbol?.Type ?? parameterSymbol?.Type ?? localSymbol?.Type; return memberType?.Name == typeName && memberType.ContainingNamespace.Equals(namespaces); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD100AsyncVoidMethodAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD100AsyncVoidMethodAnalyzer.cs index 657ac403..5837dd3f 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD100AsyncVoidMethodAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD100AsyncVoidMethodAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System; using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD101AsyncVoidLambdaAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD101AsyncVoidLambdaAnalyzer.cs index 175face3..e0f217f3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD101AsyncVoidLambdaAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD101AsyncVoidLambdaAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -73,8 +70,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers private void AnalyzeOperation(OperationAnalysisContext context) { var operation = (IAnonymousFunctionOperation)context.Operation; - var methodSymbol = operation.Symbol; - if (methodSymbol != null && methodSymbol.IsAsync && methodSymbol.ReturnsVoid) + IMethodSymbol? methodSymbol = operation.Symbol; + if (methodSymbol is object && methodSymbol.IsAsync && methodSymbol.ReturnsVoid) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, operation.Syntax.GetLocation())); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzer.cs index 0e6ae5fa..17a02f4b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs index 721d8090..6bf0bdda 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -45,8 +46,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers // Emit the diagnostic if the awaited expression is a Task or ValueTask. // They obviously aren't using ConfigureAwait in that case since the awaited expression type would be a // ConfiguredTaskAwaitable instead. - var awaitedTypeInfo = awaitOperation.Operation.Type; - if (awaitedTypeInfo != null && awaitedTypeInfo.BelongsToNamespace(Namespaces.SystemThreadingTasks) && + ITypeSymbol? awaitedTypeInfo = awaitOperation.Operation.Type; + if (awaitedTypeInfo is object && awaitedTypeInfo.BelongsToNamespace(Namespaces.SystemThreadingTasks) && (awaitedTypeInfo.Name == Types.Task.TypeName || awaitedTypeInfo.Name == Types.ValueTask.TypeName)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, awaitOperation.Operation.Syntax.GetLocation())); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD113CheckForSystemIAsyncDisposableAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD113CheckForSystemIAsyncDisposableAnalyzer.cs index 402ca92c..0a468b55 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD113CheckForSystemIAsyncDisposableAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD113CheckForSystemIAsyncDisposableAnalyzer.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { @@ -44,14 +45,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers INamedTypeSymbol? bclAsyncDisposableType = startCompilation.Compilation.GetTypeByMetadataName(Types.BclAsyncDisposable.FullName); if (vsThreadingAsyncDisposableType is object) { - startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => this.AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.IsType); - startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => this.AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.IsPattern); - startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => this.AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.Conversion); + startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.IsType); + startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.IsPattern); + startCompilation.RegisterOperationAction(Utils.DebuggableWrapper(c => AnalyzeTypeCheck(c, vsThreadingAsyncDisposableType, bclAsyncDisposableType)), OperationKind.Conversion); } }); } - private void AnalyzeTypeCheck(OperationAnalysisContext context, INamedTypeSymbol vsThreadingAsyncDisposableType, INamedTypeSymbol bclAsyncDisposableType) + private static void AnalyzeTypeCheck(OperationAnalysisContext context, INamedTypeSymbol vsThreadingAsyncDisposableType, INamedTypeSymbol bclAsyncDisposableType) { switch (context.Operation) { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD114AvoidReturningNullTaskAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD114AvoidReturningNullTaskAnalyzer.cs index 72ab2782..ded0e047 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD114AvoidReturningNullTaskAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD114AvoidReturningNullTaskAnalyzer.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers { using System.Collections.Immutable; using System.Threading.Tasks; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs index 2587ba5f..79537124 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs @@ -1,11 +1,9 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers { + using System; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; @@ -71,7 +69,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers bool hasAsyncFocusedReturnType = Utils.HasAsyncCompatibleReturnType(methodSymbol); - bool actuallyEndsWithAsync = methodSymbol.Name.EndsWith(MandatoryAsyncSuffix); + bool actuallyEndsWithAsync = methodSymbol.Name.EndsWith(MandatoryAsyncSuffix, StringComparison.CurrentCulture); if (hasAsyncFocusedReturnType != actuallyEndsWithAsync) { @@ -86,7 +84,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { // We actively encourage folks to use the Async keyword only for clearly async-focused types. // Not just any awaitable, since some stray extension method shouldn't change the world for everyone. - var properties = ImmutableDictionary.Empty + ImmutableDictionary? properties = ImmutableDictionary.Empty .Add(NewNameKey, methodSymbol.Name + MandatoryAsyncSuffix); context.ReportDiagnostic(Diagnostic.Create( AddAsyncDescriptor, @@ -96,7 +94,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers else if (!this.IsAwaitableType(methodSymbol.ReturnType, context.Compilation, context.CancellationToken)) { // Only warn about abusing the Async suffix if the return type is not awaitable. - var properties = ImmutableDictionary.Empty + ImmutableDictionary? properties = ImmutableDictionary.Empty .Add(NewNameKey, methodSymbol.Name.Substring(0, methodSymbol.Name.Length - MandatoryAsyncSuffix.Length)); context.ReportDiagnostic(Diagnostic.Create( RemoveAsyncDescriptor, diff --git a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj b/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj deleted file mode 100644 index e2c7afee..00000000 --- a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net472 - Exe - Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.ruleset - true - Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.ruleset - false - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.ruleset b/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.ruleset deleted file mode 100644 index 966d7deb..00000000 --- a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.ruleset +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.ruleset b/src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.ruleset deleted file mode 100644 index a2153bc0..00000000 --- a/src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.ruleset +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading/AsyncAutoResetEvent.cs b/src/Microsoft.VisualStudio.Threading/AsyncAutoResetEvent.cs index 44c827f0..3253611f 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncAutoResetEvent.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncAutoResetEvent.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -131,7 +128,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (toRelease != null) + if (toRelease is object) { toRelease.Registration.Dispose(); toRelease.TrySetResult(default(EmptyStruct)); diff --git a/src/Microsoft.VisualStudio.Threading/AsyncBarrier.cs b/src/Microsoft.VisualStudio.Threading/AsyncBarrier.cs index 82b6ce39..e1d6f516 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncBarrier.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncBarrier.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncCountdownEvent.cs b/src/Microsoft.VisualStudio.Threading/AsyncCountdownEvent.cs index 45b5d35c..6238fc53 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncCountdownEvent.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncCountdownEvent.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncEventHandler.cs b/src/Microsoft.VisualStudio.Threading/AsyncEventHandler.cs index 47b15363..c6eaa646 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncEventHandler.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncEventHandler.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncLazyInitializer.cs b/src/Microsoft.VisualStudio.Threading/AsyncLazyInitializer.cs index a573fa34..a0609221 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncLazyInitializer.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncLazyInitializer.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncLazy.cs b/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading/AsyncLazy.cs rename to src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs index edf203b1..edf6d104 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncLazy.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -73,7 +70,7 @@ namespace Microsoft.VisualStudio.Threading get { Interlocked.MemoryBarrier(); - return this.valueFactory == null; + return this.valueFactory is null; } } @@ -85,7 +82,7 @@ namespace Microsoft.VisualStudio.Threading get { Interlocked.MemoryBarrier(); - return this.value != null && this.value.IsCompleted; + return this.value is object && this.value.IsCompleted; } } @@ -115,14 +112,14 @@ namespace Microsoft.VisualStudio.Threading [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public Task GetValueAsync(CancellationToken cancellationToken) { - if (!((this.value != null && this.value.IsCompleted) || this.recursiveFactoryCheck.Value == null)) + if (!((this.value is object && this.value.IsCompleted) || this.recursiveFactoryCheck.Value is null)) { // PERF: we check the condition and *then* retrieve the string resource only on failure // because the string retrieval has shown up as significant on ETL traces. Verify.FailOperation(Strings.ValueFactoryReentrancy); } - if (this.value == null) + if (this.value is null) { if (Monitor.IsEntered(this.syncObject)) { @@ -139,7 +136,7 @@ namespace Microsoft.VisualStudio.Threading // then only one thread will execute the valueFactory while the // other threads synchronously block till the synchronous portion // has completed. - if (this.value == null) + if (this.value is null) { RoslynDebug.Assert(this.valueFactory is object); @@ -164,7 +161,7 @@ namespace Microsoft.VisualStudio.Threading this.recursiveFactoryCheck.Value = RecursiveCheckSentinel; try { - if (this.jobFactory != null) + if (this.jobFactory is object) { // Wrapping with RunAsync allows a future caller // to synchronously block the Main thread waiting for the result @@ -233,7 +230,7 @@ namespace Microsoft.VisualStudio.Threading // Capture the factory as a local before comparing and dereferencing it since // the field can transition to null and we want to gracefully handle that race condition. JoinableTaskFactory? factory = this.jobFactory; - return factory != null + return factory is object ? factory.Run(() => this.GetValueAsync(cancellationToken)) : this.GetValueAsync(cancellationToken).GetAwaiter().GetResult(); } @@ -244,7 +241,7 @@ namespace Microsoft.VisualStudio.Threading /// public override string ToString() { - return (this.value != null && this.value.IsCompleted) + return (this.value is object && this.value.IsCompleted) ? (this.value.Status == TaskStatus.RanToCompletion ? $"{this.value.Result}" : Strings.LazyValueFaulted) : Strings.LazyValueNotCreated; } diff --git a/src/Microsoft.VisualStudio.Threading/AsyncLocal.cs b/src/Microsoft.VisualStudio.Threading/AsyncLocal`1.cs similarity index 77% rename from src/Microsoft.VisualStudio.Threading/AsyncLocal.cs rename to src/Microsoft.VisualStudio.Threading/AsyncLocal`1.cs index d0353a63..5956a7fd 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncLocal.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncLocal`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncManualResetEvent.cs b/src/Microsoft.VisualStudio.Threading/AsyncManualResetEvent.cs index 4cfec472..4c494b05 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncManualResetEvent.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncManualResetEvent.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncQueue.cs b/src/Microsoft.VisualStudio.Threading/AsyncQueue`1.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading/AsyncQueue.cs rename to src/Microsoft.VisualStudio.Threading/AsyncQueue`1.cs index ad78a45e..15e43b20 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncQueue.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncQueue`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -111,11 +108,11 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.completedSource == null) + if (this.completedSource is null) { lock (this.SyncRoot) { - if (this.completedSource == null) + if (this.completedSource is null) { if (this.IsCompleted) { @@ -198,7 +195,7 @@ namespace Microsoft.VisualStudio.Threading if (!alreadyDispatched) { - if (this.queueElements == null) + if (this.queueElements is null) { this.queueElements = new Queue(this.InitialCapacity); } @@ -221,7 +218,7 @@ namespace Microsoft.VisualStudio.Threading { lock (this.SyncRoot) { - if (this.queueElements != null && this.queueElements.Count > 0) + if (this.queueElements is object && this.queueElements.Count > 0) { value = this.queueElements.Peek(); return true; @@ -288,7 +285,7 @@ namespace Microsoft.VisualStudio.Threading } else { - if (this.dequeuingWaiters == null) + if (this.dequeuingWaiters is null) { this.dequeuingWaiters = new Queue>(capacity: 2); } @@ -399,7 +396,7 @@ namespace Microsoft.VisualStudio.Threading bool dequeued; lock (this.SyncRoot) { - if (this.queueElements != null && this.queueElements.Count > 0 && (valueCheck == null || valueCheck(this.queueElements.Peek()))) + if (this.queueElements is object && this.queueElements.Count > 0 && (valueCheck is null || valueCheck(this.queueElements.Peek()))) { value = this.queueElements.Dequeue(); dequeued = true; @@ -429,7 +426,7 @@ namespace Microsoft.VisualStudio.Threading bool transitionTaskSource, invokeOnCompleted = false; lock (this.SyncRoot) { - transitionTaskSource = this.completeSignaled && (this.queueElements == null || this.queueElements.Count == 0); + transitionTaskSource = this.completeSignaled && (this.queueElements is null || this.queueElements.Count == 0); if (transitionTaskSource) { invokeOnCompleted = !this.onCompletedInvoked; diff --git a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock+HangReportContributor.cs b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock+HangReportContributor.cs index f48f9514..772017db 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock+HangReportContributor.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock+HangReportContributor.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -15,7 +12,7 @@ namespace Microsoft.VisualStudio.Threading using System.Threading.Tasks; using System.Xml.Linq; - partial class AsyncReaderWriterLock : IHangReportContributor + public partial class AsyncReaderWriterLock : IHangReportContributor { [Flags] private enum AwaiterCollection @@ -70,7 +67,7 @@ namespace Microsoft.VisualStudio.Threading try { Monitor.TryEnter(this.syncObject, 1000, ref lockAcquired); - var dgml = CreateDgml(out XElement nodes, out XElement links); + XDocument? dgml = CreateDgml(out XElement nodes, out XElement links); if (!lockAcquired) { @@ -85,7 +82,7 @@ namespace Microsoft.VisualStudio.Threading liveAwaiterMetadata.UnionWith(this.issuedUpgradeableReadLocks.Select(a => new AwaiterMetadata(a, AwaiterCollection.Issued | AwaiterCollection.UpgradeableReadLock))); liveAwaiterMetadata.UnionWith(this.issuedWriteLocks.Select(a => new AwaiterMetadata(a, AwaiterCollection.Issued | AwaiterCollection.WriteLock))); - var liveAwaiters = liveAwaiterMetadata.Select(am => am.Awaiter); + IEnumerable? liveAwaiters = liveAwaiterMetadata.Select(am => am.Awaiter); var releasedAwaiterMetadata = new HashSet(liveAwaiters.SelectMany(GetLockStack).Distinct().Except(liveAwaiters).Select(AwaiterMetadata.Released)); var allAwaiterMetadata = new HashSet(liveAwaiterMetadata.Concat(releasedAwaiterMetadata)); @@ -96,7 +93,7 @@ namespace Microsoft.VisualStudio.Threading nodes.Add(allAwaiterMetadata.Select(am => CreateAwaiterNode(am.Awaiter).WithCategories(am.Categories.ToArray()).ContainedBy(am.GroupId, dgml))); // Link the lock stacks among themselves. - links.Add(allAwaiterMetadata.Where(a => a.Awaiter.NestingLock != null).Select(a => Dgml.Link(GetAwaiterId(a.Awaiter.NestingLock!), GetAwaiterId(a.Awaiter)))); + links.Add(allAwaiterMetadata.Where(a => a.Awaiter.NestingLock is object).Select(a => Dgml.Link(GetAwaiterId(a.Awaiter.NestingLock!), GetAwaiterId(a.Awaiter)))); return new HangReportContribution( dgml.ToString(), @@ -141,12 +138,12 @@ namespace Microsoft.VisualStudio.Threading } Delegate? lockWaitingContinuation; - if (awaiter.RequestingStackTrace != null) + if (awaiter.RequestingStackTrace is object) { label.AppendLine(awaiter.RequestingStackTrace.ToString()); } - if ((lockWaitingContinuation = awaiter.LockRequestingContinuation) != null) + if ((lockWaitingContinuation = awaiter.LockRequestingContinuation) is object) { try { @@ -180,7 +177,7 @@ namespace Microsoft.VisualStudio.Threading private static string GetAwaiterGroupId(Awaiter awaiter) { Requires.NotNull(awaiter, nameof(awaiter)); - while (awaiter.NestingLock != null) + while (awaiter.NestingLock is object) { awaiter = awaiter.NestingLock; } @@ -191,7 +188,7 @@ namespace Microsoft.VisualStudio.Threading private static IEnumerable GetLockStack(Awaiter awaiter) { Requires.NotNull(awaiter, nameof(awaiter)); - for (Awaiter? current = awaiter; current != null; current = current.NestingLock) + for (Awaiter? current = awaiter; current is object; current = current.NestingLock) { yield return awaiter; } @@ -240,14 +237,14 @@ namespace Microsoft.VisualStudio.Threading public override bool Equals(object? obj) { var otherAwaiter = obj as AwaiterMetadata; - return otherAwaiter != null && this.Awaiter.Equals(otherAwaiter.Awaiter); + return otherAwaiter is object && this.Awaiter.Equals(otherAwaiter.Awaiter); } internal static AwaiterMetadata Released(Awaiter awaiter) { Requires.NotNull(awaiter, nameof(awaiter)); - var membership = AwaiterCollection.Released; + AwaiterCollection membership = AwaiterCollection.Released; switch (awaiter.Kind) { case LockKind.Read: diff --git a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock.cs b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock.cs index 6670683e..6b4aab58 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterLock.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -365,8 +362,8 @@ namespace Microsoft.VisualStudio.Threading { get { - var ctxt = SynchronizationContext.Current; - bool supported = ctxt == null || ctxt is NonConcurrentSynchronizationContext; + SynchronizationContext? ctxt = SynchronizationContext.Current; + bool supported = ctxt is null || ctxt is NonConcurrentSynchronizationContext; return !supported; } } @@ -529,11 +526,11 @@ namespace Microsoft.VisualStudio.Threading { LockFlags aggregateFlags = LockFlags.None; Awaiter? awaiter = handle.Awaiter; - if (awaiter != null) + if (awaiter is object) { lock (this.syncObject) { - while (awaiter != null) + while (awaiter is object) { if (this.IsLockActive(awaiter, considerStaActive: true, checkSyncContextCompatibility: true)) { @@ -565,12 +562,12 @@ namespace Microsoft.VisualStudio.Threading protected LockFlags GetAggregateLockFlags() { LockFlags aggregateFlags = LockFlags.None; - var awaiter = this.topAwaiter.Value; - if (awaiter != null) + Awaiter? awaiter = this.topAwaiter.Value; + if (awaiter is object) { lock (this.syncObject) { - while (awaiter != null) + while (awaiter is object) { if (this.IsLockActive(awaiter, considerStaActive: true, checkSyncContextCompatibility: true)) { @@ -644,6 +641,90 @@ namespace Microsoft.VisualStudio.Threading return TaskScheduler.Default; } + /// + /// Invoked after an exclusive lock is released but before anyone has a chance to enter the lock. + /// + /// + /// This method is called while holding a private lock in order to block future lock consumers till this method is finished. + /// + protected virtual Task OnExclusiveLockReleasedAsync() + { + return Task.CompletedTask; + } + + /// + /// Invoked when a top-level upgradeable read lock is released, leaving no remaining (write) lock. + /// + protected virtual void OnUpgradeableReadLockReleased() + { + } + + /// + /// Invoked when the lock detects an internal error or illegal usage pattern that + /// indicates a serious flaw that should be immediately reported to the application + /// and/or bring down the process to avoid hangs or data corruption. + /// + /// The exception that captures the details of the failure. + /// An exception that may be returned by some implementations of tis method for he caller to rethrow. + protected virtual Exception OnCriticalFailure(Exception ex) + { + Requires.NotNull(ex, nameof(ex)); + + Report.Fail(ex.Message); + Environment.FailFast(ex.ToString(), ex); + throw Assumes.NotReachable(); + } + + /// + /// Invoked when the lock detects an internal error or illegal usage pattern that + /// indicates a serious flaw that should be immediately reported to the application + /// and/or bring down the process to avoid hangs or data corruption. + /// + /// The message to use for the exception. + /// An exception that may be returned by some implementations of tis method for he caller to rethrow. + protected Exception OnCriticalFailure(string message) + { + try + { + throw Assumes.Fail(message); + } + catch (Exception ex) + { + throw this.OnCriticalFailure(ex); + } + } + + /// + /// Checks whether the specified lock has any active nested locks. + /// + private static bool HasAnyNestedLocks(Awaiter lck, HashSet lockCollection) + { + Requires.NotNull(lck, nameof(lck)); + Requires.NotNull(lockCollection, nameof(lockCollection)); + + if (lockCollection.Count > 0) + { + foreach (Awaiter? nestedCandidate in lockCollection) + { + if (nestedCandidate == lck) + { + // This isn't nested -- it's the lock itself. + continue; + } + + for (Awaiter? a = nestedCandidate.NestingLock; a is object; a = a.NestingLock) + { + if (a == lck) + { + return true; + } + } + } + } + + return false; + } + /// /// Throws an exception if called on an STA thread. /// @@ -695,7 +776,7 @@ namespace Microsoft.VisualStudio.Threading if (this.completeInvoked && !this.completionSource.Task.IsCompleted && - this.reenterConcurrencyPrepRunning == null && + this.reenterConcurrencyPrepRunning is null && this.issuedReadLocks.Count == 0 && this.issuedUpgradeableReadLocks.Count == 0 && this.issuedWriteLocks.Count == 0 && this.waitingReaders.Count == 0 && this.waitingUpgradeableReaders.Count == 0 && this.waitingWriters.Count == 0) { @@ -718,11 +799,11 @@ namespace Microsoft.VisualStudio.Threading upgradeableRead = false; write = false; - if (awaiter != null) + if (awaiter is object) { lock (this.syncObject) { - while (awaiter != null) + while (awaiter is object) { // It's possible that this lock has been released (even mid-stack, due to our async nature), // so only consider locks that are still active. @@ -759,13 +840,13 @@ namespace Microsoft.VisualStudio.Threading /// true if all issued locks are the specified lock or nesting locks of it. private bool AllHeldLocksAreByThisStack(Awaiter? awaiter) { - Assumes.True(awaiter == null || !this.IsLockHeld(LockKind.Write, awaiter)); // this method doesn't yet handle sticky upgraded read locks (that appear in the write lock set). + Assumes.True(awaiter is null || !this.IsLockHeld(LockKind.Write, awaiter)); // this method doesn't yet handle sticky upgraded read locks (that appear in the write lock set). lock (this.syncObject) { - if (awaiter != null) + if (awaiter is object) { int locksMatched = 0; - while (awaiter != null) + while (awaiter is object) { if (this.GetActiveLockSet(awaiter.Kind).Contains(awaiter)) { @@ -792,12 +873,12 @@ namespace Microsoft.VisualStudio.Threading /// true if the lock holder (also) holds the specified kind of lock. private bool LockStackContains(LockKind kind, Awaiter? awaiter) { - if (awaiter != null) + if (awaiter is object) { lock (this.syncObject) { - var lockSet = this.GetActiveLockSet(kind); - while (awaiter != null) + HashSet? lockSet = this.GetActiveLockSet(kind); + while (awaiter is object) { // It's possible that this lock has been released (even mid-stack, due to our async nature), // so only consider locks that are still active. @@ -929,7 +1010,7 @@ namespace Microsoft.VisualStudio.Threading if (this.completeInvoked && !previouslyQueued) { // If this is a new top-level lock request, reject it completely. - if (awaiter.NestingLock == null) + if (awaiter.NestingLock is null) { awaiter.SetFault(new InvalidOperationException(Strings.LockCompletionAlreadyRequested)); return false; @@ -937,7 +1018,7 @@ namespace Microsoft.VisualStudio.Threading } bool issued = false; - if (this.reenterConcurrencyPrepRunning == null) + if (this.reenterConcurrencyPrepRunning is null) { if (this.issuedWriteLocks.Count == 0 && this.issuedUpgradeableReadLocks.Count == 0 && this.issuedReadLocks.Count == 0) { @@ -1005,8 +1086,8 @@ namespace Microsoft.VisualStudio.Threading { issued = true; - var stickyWriteAwaiter = this.FindRootUpgradeableReadWithStickyWrite(awaiter); - if (stickyWriteAwaiter != null) + Awaiter? stickyWriteAwaiter = this.FindRootUpgradeableReadWithStickyWrite(awaiter); + if (stickyWriteAwaiter is object) { // Add the upgradeable reader as a write lock as well. this.issuedWriteLocks.Add(stickyWriteAwaiter); @@ -1047,13 +1128,13 @@ namespace Microsoft.VisualStudio.Threading /// The least nested upgradeable reader lock with sticky write flag; or null if none was found. private Awaiter? FindRootUpgradeableReadWithStickyWrite(Awaiter? headAwaiter) { - if (headAwaiter == null) + if (headAwaiter is null) { return null; } - var lowerMatch = this.FindRootUpgradeableReadWithStickyWrite(headAwaiter.NestingLock); - if (lowerMatch != null) + Awaiter? lowerMatch = this.FindRootUpgradeableReadWithStickyWrite(headAwaiter.NestingLock); + if (lowerMatch is object) { return lowerMatch; } @@ -1119,7 +1200,7 @@ namespace Microsoft.VisualStudio.Threading /// The first active lock encountered, or null if none. private Awaiter? GetFirstActiveSelfOrAncestor(Awaiter? awaiter) { - while (awaiter != null) + while (awaiter is object) { if (this.IsLockActive(awaiter, considerStaActive: true)) { @@ -1144,90 +1225,6 @@ namespace Microsoft.VisualStudio.Threading Assumes.True(this.ExecuteOrHandleCancellation(awaiter, stillInQueue: false)); } - /// - /// Invoked after an exclusive lock is released but before anyone has a chance to enter the lock. - /// - /// - /// This method is called while holding a private lock in order to block future lock consumers till this method is finished. - /// - protected virtual Task OnExclusiveLockReleasedAsync() - { - return Task.CompletedTask; - } - - /// - /// Invoked when a top-level upgradeable read lock is released, leaving no remaining (write) lock. - /// - protected virtual void OnUpgradeableReadLockReleased() - { - } - - /// - /// Invoked when the lock detects an internal error or illegal usage pattern that - /// indicates a serious flaw that should be immediately reported to the application - /// and/or bring down the process to avoid hangs or data corruption. - /// - /// The exception that captures the details of the failure. - /// An exception that may be returned by some implementations of tis method for he caller to rethrow. - protected virtual Exception OnCriticalFailure(Exception ex) - { - Requires.NotNull(ex, nameof(ex)); - - Report.Fail(ex.Message); - Environment.FailFast(ex.ToString(), ex); - throw Assumes.NotReachable(); - } - - /// - /// Invoked when the lock detects an internal error or illegal usage pattern that - /// indicates a serious flaw that should be immediately reported to the application - /// and/or bring down the process to avoid hangs or data corruption. - /// - /// The message to use for the exception. - /// An exception that may be returned by some implementations of tis method for he caller to rethrow. - protected Exception OnCriticalFailure(string message) - { - try - { - throw Assumes.Fail(message); - } - catch (Exception ex) - { - throw this.OnCriticalFailure(ex); - } - } - - /// - /// Checks whether the specified lock has any active nested locks. - /// - private static bool HasAnyNestedLocks(Awaiter lck, HashSet lockCollection) - { - Requires.NotNull(lck, nameof(lck)); - Requires.NotNull(lockCollection, nameof(lockCollection)); - - if (lockCollection.Count > 0) - { - foreach (var nestedCandidate in lockCollection) - { - if (nestedCandidate == lck) - { - // This isn't nested -- it's the lock itself. - continue; - } - - for (Awaiter? a = nestedCandidate.NestingLock; a != null; a = a.NestingLock) - { - if (a == lck) - { - return true; - } - } - } - } - - return false; - } - /// /// Releases the lock held by the specified awaiter. /// @@ -1255,7 +1252,7 @@ namespace Microsoft.VisualStudio.Threading // access to concurrent access while someone is actually holding a lock (as such transition requires // the lock class itself to have the exclusive lock to protect the resources going through the transition). Awaiter? illegalConcurrentLock = this.reenterConcurrencyPrepRunning; // capture to local to preserve evidence in a concurrently reset field. - if (illegalConcurrentLock != null) + if (illegalConcurrentLock is object) { try { @@ -1326,7 +1323,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (reenterConcurrentOutsideCode == null) + if (reenterConcurrentOutsideCode is null) { this.OnReleaseReenterConcurrencyComplete(awaiter, upgradedStickyWrite, searchAllWaiters: false); } @@ -1337,7 +1334,7 @@ namespace Microsoft.VisualStudio.Threading // Updating the topAwaiter requires touching the CallContext, which significantly increases the perf/GC hit // for releasing locks. So we prefer to leave a released lock in the context and walk up the lock stack when // necessary. But we will clean it up if it's the last lock released. - if (remainingAwaiter == null) + if (remainingAwaiter is null) { // This assignment is outside the lock because it doesn't need the lock and it's a relatively expensive call // that we needn't hold the lock for. @@ -1346,7 +1343,7 @@ namespace Microsoft.VisualStudio.Threading if (synchronousRequired || true) { // the "|| true" bit is to force us to always be synchronous when releasing locks until we can get all tests passing the other way. - if (reenterConcurrentOutsideCode != null && (synchronousCallbackExecution != null && !synchronousCallbackExecution.IsCompleted)) + if (reenterConcurrentOutsideCode is object && (synchronousCallbackExecution is object && !synchronousCallbackExecution.IsCompleted)) { return Task.WhenAll(reenterConcurrentOutsideCode, synchronousCallbackExecution); } @@ -1374,7 +1371,9 @@ namespace Microsoft.VisualStudio.Threading { await beginAfterPrerequisite.ConfigureAwait(SynchronizationContext.Current is NonConcurrentSynchronizationContext); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { prereqException = ex; } @@ -1409,7 +1408,9 @@ namespace Microsoft.VisualStudio.Threading { await onExclusiveLockReleasedTask.ConfigureAwait(false); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { onExclusiveLockReleasedTaskException = ex; } @@ -1431,13 +1432,13 @@ namespace Microsoft.VisualStudio.Threading this.OnReleaseReenterConcurrencyComplete(awaiter, upgradedStickyWrite, searchAllWaiters: true); } - if (prereqException != null) + if (prereqException is object) { // rethrow the exception we experienced before, such that it doesn't wipe out its callstack. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(prereqException).Throw(); } - if (onExclusiveLockReleasedTaskException != null) + if (onExclusiveLockReleasedTaskException is object) { // rethrow the exception we experienced before, such that it doesn't wipe out its callstack. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(onExclusiveLockReleasedTaskException).Throw(); @@ -1518,9 +1519,11 @@ namespace Microsoft.VisualStudio.Threading { await callback().ConfigureAwait(true); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - if (exceptions == null) + if (exceptions is null) { exceptions = new List(); } @@ -1529,7 +1532,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (exceptions != null) + if (exceptions is object) { throw new AggregateException(exceptions); } @@ -1564,7 +1567,7 @@ namespace Microsoft.VisualStudio.Threading /// The awaiter that tracks the lock to grant to the caller. private void ApplyLockToCallContext(Awaiter? topAwaiter) { - var awaiter = this.GetFirstActiveSelfOrAncestor(topAwaiter); + Awaiter? awaiter = this.GetFirstActiveSelfOrAncestor(topAwaiter); this.topAwaiter.Value = awaiter; } @@ -1580,7 +1583,7 @@ namespace Microsoft.VisualStudio.Threading { while (this.waitingReaders.Count > 0) { - var pendingReader = this.waitingReaders.Dequeue(); + Awaiter? pendingReader = this.waitingReaders.Dequeue(); Assumes.True(pendingReader.Kind == LockKind.Read); this.IssueAndExecute(pendingReader); invoked = true; @@ -1608,7 +1611,7 @@ namespace Microsoft.VisualStudio.Threading { if (this.waitingUpgradeableReaders.Count > 0) { - var pendingUpgradeableReader = this.waitingUpgradeableReaders.Dequeue(); + Awaiter? pendingUpgradeableReader = this.waitingUpgradeableReaders.Dequeue(); Assumes.True(pendingUpgradeableReader.Kind == LockKind.UpgradeableRead); this.IssueAndExecute(pendingUpgradeableReader); return true; @@ -1637,7 +1640,7 @@ namespace Microsoft.VisualStudio.Threading { if (this.waitingWriters.Count > 0) { - var pendingWriter = this.waitingWriters.Dequeue(); + Awaiter? pendingWriter = this.waitingWriters.Dequeue(); Assumes.True(pendingWriter.Kind == LockKind.Write); this.IssueAndExecute(pendingWriter); return true; @@ -1669,7 +1672,7 @@ namespace Microsoft.VisualStudio.Threading do { invokedThisLoop = false; - foreach (var lockWaiter in waiters) + foreach (Awaiter? lockWaiter in waiters) { if (this.TryIssueLock(lockWaiter, previouslyQueued: true)) { @@ -1691,7 +1694,8 @@ namespace Microsoft.VisualStudio.Threading break; } } - } while (invokedThisLoop); // keep looping while we find matching locks. + } + while (invokedThisLoop); // keep looping while we find matching locks. return invoked; } @@ -1712,7 +1716,7 @@ namespace Microsoft.VisualStudio.Threading } else { - var queue = this.GetLockQueue(awaiter.Kind); + Queue? queue = this.GetLockQueue(awaiter.Kind); queue.Enqueue(awaiter); } } @@ -1734,7 +1738,7 @@ namespace Microsoft.VisualStudio.Threading { // The lock class can't deal well with cancelled lock requests remaining in its queue. // Remove the awaiter, wherever in the queue it happens to be. - var queue = this.GetLockQueue(awaiter.Kind); + Queue? queue = this.GetLockQueue(awaiter.Kind); if (!queue.RemoveMidQueue(awaiter)) { // This can happen when the lock request is cancelled, but during a race @@ -1783,9 +1787,11 @@ namespace Microsoft.VisualStudio.Threading /// /// Gets the awaiter value. /// +#pragma warning disable CA1034 // Nested types should not be visible public Awaiter GetAwaiter() +#pragma warning restore CA1034 // Nested types should not be visible { - if (this.awaiter == null) + if (this.awaiter is null) { throw new InvalidOperationException(); } @@ -1798,7 +1804,9 @@ namespace Microsoft.VisualStudio.Threading /// A value whose disposal releases a held lock. /// [DebuggerDisplay("{awaiter.kind}")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct Releaser : IDisposable, System.IAsyncDisposable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The awaiter who manages the lifetime of a lock. @@ -1819,7 +1827,7 @@ namespace Microsoft.VisualStudio.Threading /// public void Dispose() { - if (this.awaiter != null) + if (this.awaiter is object) { var nonConcurrentSyncContext = SynchronizationContext.Current as NonConcurrentSynchronizationContext; @@ -1829,9 +1837,9 @@ namespace Microsoft.VisualStudio.Threading // blocked and wait until it is done, and that makes it possible to run into the thread pool exhaustion trap. if (!this.awaiter.IsReleased) { - using (nonConcurrentSyncContext != null ? nonConcurrentSyncContext.LoanBackAnyHeldResource(this.awaiter.OwningLock) : default(NonConcurrentSynchronizationContext.LoanBack)) + using (nonConcurrentSyncContext is object ? nonConcurrentSyncContext.LoanBackAnyHeldResource(this.awaiter.OwningLock) : default(NonConcurrentSynchronizationContext.LoanBack)) { - var releaseTask = this.awaiter.ReleaseAsync(); + Task? releaseTask = this.awaiter.ReleaseAsync(); using (NoMessagePumpSyncContext.Default.Apply()) { try @@ -1849,7 +1857,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (nonConcurrentSyncContext != null && !this.awaiter.OwningLock.AmbientLock.IsValid) + if (nonConcurrentSyncContext is object && !this.awaiter.OwningLock.AmbientLock.IsValid) { // The lock holder is taking the synchronous path to release the last UR/W lock held. // Since they may go synchronously on their merry way for a while, forcibly release @@ -1881,10 +1889,12 @@ namespace Microsoft.VisualStudio.Threading /// public Task ReleaseAsync() { - if (this.awaiter != null) + if (this.awaiter is object) { var nonConcurrentSyncContext = SynchronizationContext.Current as NonConcurrentSynchronizationContext; - using (nonConcurrentSyncContext != null ? nonConcurrentSyncContext.LoanBackAnyHeldResource(this.awaiter.OwningLock) : default(NonConcurrentSynchronizationContext.LoanBack)) +#pragma warning disable CA1034 // Nested types should not be visible + using (nonConcurrentSyncContext is object ? nonConcurrentSyncContext.LoanBackAnyHeldResource(this.awaiter.OwningLock) : default(NonConcurrentSynchronizationContext.LoanBack)) +#pragma warning restore CA1034 // Nested types should not be visible { return this.awaiter.ReleaseAsync(); } @@ -1897,7 +1907,9 @@ namespace Microsoft.VisualStudio.Threading /// /// A value whose disposal restores visibility of any locks held by the caller. /// +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct Suppression : IDisposable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The locking class. @@ -1917,7 +1929,7 @@ namespace Microsoft.VisualStudio.Threading { this.lck = lck; this.awaiter = this.lck.topAwaiter.Value; - if (this.awaiter != null) + if (this.awaiter is object) { this.lck.topAwaiter.Value = null; } @@ -1928,7 +1940,7 @@ namespace Microsoft.VisualStudio.Threading /// public void Dispose() { - if (this.lck != null) + if (this.lck is object) { this.lck.ApplyLockToCallContext(this.awaiter); } @@ -1958,7 +1970,7 @@ namespace Microsoft.VisualStudio.Threading /// public bool IsValid { - get { return this.awaiter != null; } + get { return this.awaiter is object; } } /// @@ -2047,7 +2059,9 @@ namespace Microsoft.VisualStudio.Threading /// public LockHandle NestingLock { +#pragma warning disable CA1034 // Nested types should not be visible get { return this.IsValid ? new LockHandle(this.awaiter!.NestingLock) : default(LockHandle); } +#pragma warning restore CA1034 // Nested types should not be visible } /// @@ -2063,10 +2077,10 @@ namespace Microsoft.VisualStudio.Threading /// Manages asynchronous access to a lock. /// [DebuggerDisplay("{kind}")] +#pragma warning disable CA1034 // Nested types should not be visible public class Awaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible { -#region Fields - /// /// A singleton delegate for use in cancellation token registration to avoid memory allocations for delegates each time. /// @@ -2149,8 +2163,6 @@ namespace Microsoft.VisualStudio.Threading /// private object? data; -#endregion - /// /// Initializes a new instance of the class. /// @@ -2177,7 +2189,7 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.fault != null) + if (this.fault is object) { return true; } @@ -2268,7 +2280,7 @@ namespace Microsoft.VisualStudio.Threading { get { - return this.releaseAsyncTask != null && this.releaseAsyncTask.Status == TaskStatus.RanToCompletion; + return this.releaseAsyncTask is object && this.releaseAsyncTask.Status == TaskStatus.RanToCompletion; } } @@ -2304,7 +2316,7 @@ namespace Microsoft.VisualStudio.Threading { this.cancellationRegistration.Dispose(); - if (!this.LockIssued && this.continuation == null && !this.cancellationToken.IsCancellationRequested) + if (!this.LockIssued && this.continuation is null && !this.cancellationToken.IsCancellationRequested) { using (var synchronousBlock = new ManualResetEventSlim()) { @@ -2313,7 +2325,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (this.fault != null) + if (this.fault is object) { throw this.fault; } @@ -2360,7 +2372,7 @@ namespace Microsoft.VisualStudio.Threading /// internal Task ReleaseAsync(bool lockConsumerCanceled = false) { - if (this.releaseAsyncTask == null) + if (this.releaseAsyncTask is null) { // This method does NOT use the async keyword in its signature to avoid CallContext changes that we make // causing a fork/clone of the CallContext, which defeats our alloc-free uncontested lock story. @@ -2390,14 +2402,14 @@ namespace Microsoft.VisualStudio.Threading /// true if the continuation was (asynchronously) invoked; false if there was no continuation available to invoke. internal bool TryScheduleContinuationExecution() { - var continuation = Interlocked.Exchange(ref this.continuation, null); + Action? continuation = Interlocked.Exchange(ref this.continuation, null); - if (continuation != null) + if (continuation is object) { this.continuationAfterLockIssued = continuation; - var synchronizationContext = this.GetEffectiveSynchronizationContext(); - if (this.continuationTaskScheduler != null && synchronizationContext == DefaultSynchronizationContext) + SynchronizationContext? synchronizationContext = this.GetEffectiveSynchronizationContext(); + if (this.continuationTaskScheduler is object && synchronizationContext == DefaultSynchronizationContext) { Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, this.continuationTaskScheduler); } @@ -2422,52 +2434,6 @@ namespace Microsoft.VisualStudio.Threading this.fault = ex; } - /// - /// Get the correct SynchronizationContext to execute code executing within the lock. - /// Note: we need get the NonConcurrentSynchronizationContext from the nesting exclusive lock, because the child lock is essentially under the same context. - /// When we don't have a valid nesting lock, we will create a new NonConcurrentSynchronizationContext for an exclusive lock. For read lock, we don't put it within a NonConcurrentSynchronizationContext, - /// we set it to DefaultSynchronizationContext to mark we have computed it. The result is cached. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "NonConcurrentSynchronizationContext is shared among locks, and cannot be disposed.")] - private SynchronizationContext GetEffectiveSynchronizationContext() - { - if (this.synchronizationContext == null) - { - // Only read locks can be executed trivially. The locks that have some level of exclusivity (upgradeable read and write) - // must be executed via the NonConcurrentSynchronizationContext. - SynchronizationContext? synchronizationContext = null; - - Awaiter? awaiter = this.NestingLock; - while (awaiter != null) - { - if (this.lck.IsLockActive(awaiter, considerStaActive: true)) - { - synchronizationContext = awaiter.GetEffectiveSynchronizationContext(); - break; - } - - awaiter = awaiter.NestingLock; - } - - if (synchronizationContext == null) - { - if (this.kind == LockKind.Read) - { - // We use DefaultSynchronizationContext to indicate that we have already computed the synchronizationContext once, and prevent repeating this logic second time. - synchronizationContext = DefaultSynchronizationContext; - } - else - { - synchronizationContext = new NonConcurrentSynchronizationContext(); - } - } - - Interlocked.CompareExchange(ref this.synchronizationContext, synchronizationContext, null); - } - - return this.synchronizationContext; - } - /// /// Responds to lock request cancellation. /// @@ -2495,6 +2461,52 @@ namespace Microsoft.VisualStudio.Threading awaiter.cancellationRegistration.Dispose(); } + /// + /// Get the correct SynchronizationContext to execute code executing within the lock. + /// Note: we need get the NonConcurrentSynchronizationContext from the nesting exclusive lock, because the child lock is essentially under the same context. + /// When we don't have a valid nesting lock, we will create a new NonConcurrentSynchronizationContext for an exclusive lock. For read lock, we don't put it within a NonConcurrentSynchronizationContext, + /// we set it to DefaultSynchronizationContext to mark we have computed it. The result is cached. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "NonConcurrentSynchronizationContext is shared among locks, and cannot be disposed.")] + private SynchronizationContext GetEffectiveSynchronizationContext() + { + if (this.synchronizationContext is null) + { + // Only read locks can be executed trivially. The locks that have some level of exclusivity (upgradeable read and write) + // must be executed via the NonConcurrentSynchronizationContext. + SynchronizationContext? synchronizationContext = null; + + Awaiter? awaiter = this.NestingLock; + while (awaiter is object) + { + if (this.lck.IsLockActive(awaiter, considerStaActive: true)) + { + synchronizationContext = awaiter.GetEffectiveSynchronizationContext(); + break; + } + + awaiter = awaiter.NestingLock; + } + + if (synchronizationContext is null) + { + if (this.kind == LockKind.Read) + { + // We use DefaultSynchronizationContext to indicate that we have already computed the synchronizationContext once, and prevent repeating this logic second time. + synchronizationContext = DefaultSynchronizationContext; + } + else + { + synchronizationContext = new NonConcurrentSynchronizationContext(); + } + } + + Interlocked.CompareExchange(ref this.synchronizationContext, synchronizationContext, null); + } + + return this.synchronizationContext; + } + /// /// Sets the delegate to execute when the lock is available. /// @@ -2507,7 +2519,7 @@ namespace Microsoft.VisualStudio.Threading throw new InvalidOperationException(); } - if (Interlocked.CompareExchange(ref this.continuation, continuation, null) != null) + if (Interlocked.CompareExchange(ref this.continuation, continuation, null) is object) { throw new NotSupportedException(Strings.MultipleContinuationsNotSupported); } @@ -2654,7 +2666,9 @@ namespace Microsoft.VisualStudio.Threading delegateInvoked = true; // set now, before the delegate might throw. d(state); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { // We just eat these up to avoid crashing the process by throwing on a threadpool thread. Report.Fail("An unhandled exception was thrown from within a posted message. {0}", ex); @@ -2683,7 +2697,9 @@ namespace Microsoft.VisualStudio.Threading delegateInvoked = true; // set now, before the delegate might throw. d(state); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { // We just eat these up to avoid crashing the process by throwing on a threadpool thread. Report.Fail("An unhandled exception was thrown from within a posted message. {0}", ex); @@ -2709,7 +2725,7 @@ namespace Microsoft.VisualStudio.Threading public void Dispose() { - if (this.syncContext != null) + if (this.syncContext is object) { Assumes.False(Monitor.IsEntered(this.asyncLock.syncObject), "Should not wait on the Semaphore, when we hold the syncObject. This causes deadlocks"); this.syncContext.semaphore.Wait(); @@ -2729,6 +2745,14 @@ namespace Microsoft.VisualStudio.Threading this.lck = lck; } + internal static void WaitStop(Awaiter lckAwaiter) + { + if (ThreadingEventSource.Instance.IsEnabled()) + { + ThreadingEventSource.Instance.WaitReaderWriterLockStop(lckAwaiter.GetHashCode(), lckAwaiter.Kind); + } + } + internal void Issued(Awaiter lckAwaiter) { if (ThreadingEventSource.Instance.IsEnabled()) @@ -2744,14 +2768,6 @@ namespace Microsoft.VisualStudio.Threading ThreadingEventSource.Instance.WaitReaderWriterLockStart(lckAwaiter.GetHashCode(), lckAwaiter.Kind, this.lck.issuedWriteLocks.Count, this.lck.issuedUpgradeableReadLocks.Count, this.lck.issuedReadLocks.Count); } } - - internal static void WaitStop(Awaiter lckAwaiter) - { - if (ThreadingEventSource.Instance.IsEnabled()) - { - ThreadingEventSource.Instance.WaitReaderWriterLockStop(lckAwaiter.GetHashCode(), lckAwaiter.Kind); - } - } } } } diff --git a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock.cs b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock`2.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock.cs rename to src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock`2.cs index 1fd95109..8677b868 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncReaderWriterResourceLock`2.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -260,7 +257,9 @@ namespace Microsoft.VisualStudio.Threading /// An awaitable that is returned from asynchronous lock requests. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct ResourceAwaitable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The underlying lock awaitable. @@ -298,7 +297,9 @@ namespace Microsoft.VisualStudio.Threading /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] [DebuggerDisplay("{awaiter.kind}")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct ResourceAwaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The underlying lock awaiter. @@ -331,7 +332,7 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.awaiter == null) + if (this.awaiter is null) { throw new InvalidOperationException(); } @@ -346,7 +347,7 @@ namespace Microsoft.VisualStudio.Threading /// The delegate. public void OnCompleted(Action continuation) { - if (this.awaiter == null) + if (this.awaiter is null) { throw new InvalidOperationException(); } @@ -360,7 +361,7 @@ namespace Microsoft.VisualStudio.Threading /// The delegate. public void UnsafeOnCompleted(Action continuation) { - if (this.awaiter == null) + if (this.awaiter is null) { throw new InvalidOperationException(); } @@ -375,7 +376,7 @@ namespace Microsoft.VisualStudio.Threading [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] public ResourceReleaser GetResult() { - if (this.awaiter == null) + if (this.awaiter is null) { throw new InvalidOperationException(); } @@ -389,7 +390,9 @@ namespace Microsoft.VisualStudio.Threading /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] [DebuggerDisplay("{releaser.awaiter.kind}")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct ResourceReleaser : IDisposable, System.IAsyncDisposable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The underlying lock releaser. @@ -552,7 +555,7 @@ namespace Microsoft.VisualStudio.Threading // to reduce the number of slow AsyncLocal.get_Value calls we make. // Also do it before we acquire the lock, since a lock isn't necessary. // (verified to be a perf bottleneck in ETL traces). - var ambientLock = this.service.AmbientLock; + LockHandle ambientLock = this.service.AmbientLock; lock (this.service.SyncObject) { if (!ambientLock.HasWriteLock && ambientLock.HasUpgradeableReadLock) @@ -578,13 +581,13 @@ namespace Microsoft.VisualStudio.Threading // to reduce the number of slow AsyncLocal.get_Value calls we make. // Also do it before we acquire the lock, since a lock isn't necessary. // (verified to be a perf bottleneck in ETL traces). - var ambientLock = this.service.AmbientLock; + LockHandle ambientLock = this.service.AmbientLock; bool match = false; lock (this.service.SyncObject) { if (ambientLock.HasWriteLock || ambientLock.HasUpgradeableReadLock) { - foreach (var resource in this.resourcePreparationTasks) + foreach (KeyValuePair.Helper.ResourcePreparationTaskAndValidity> resource in this.resourcePreparationTasks) { if (resourceCheck(resource.Key, state)) { @@ -616,7 +619,7 @@ namespace Microsoft.VisualStudio.Threading // because as soon as this method returns these resources may be access concurrently again. var preparationTasks = new Task[this.resourcesAcquiredWithinUpgradeableRead.Count]; int taskIndex = 0; - foreach (var resource in this.resourcesAcquiredWithinUpgradeableRead) + foreach (TResource? resource in this.resourcesAcquiredWithinUpgradeableRead) { preparationTasks[taskIndex++] = this.PrepareResourceAsync(resource, CancellationToken.None, forcePrepareConcurrent: true); } @@ -651,9 +654,9 @@ namespace Microsoft.VisualStudio.Threading /// A task whose result is the desired resource. internal async Task GetResourceAsync(TMoniker resourceMoniker, CancellationToken cancellationToken) { - using (var resourceLock = this.AcquirePreexistingLockOrThrow()) + using (AsyncReaderWriterResourceLock.ResourceReleaser resourceLock = this.AcquirePreexistingLockOrThrow()) { - var resource = await this.service.GetResourceAsync(resourceMoniker, cancellationToken).ConfigureAwait(false); + TResource? resource = await this.service.GetResourceAsync(resourceMoniker, cancellationToken).ConfigureAwait(false); Task preparationTask; lock (this.service.SyncObject) @@ -701,7 +704,7 @@ namespace Microsoft.VisualStudio.Threading private void SetUnknownResourceState(IEnumerable resources) { Requires.NotNull(resources, nameof(resources)); - foreach (var resource in resources) + foreach (TResource? resource in resources) { this.SetUnknownResourceState(resource); } @@ -723,14 +726,14 @@ namespace Microsoft.VisualStudio.Threading // across requests and we can't have task continuation chains where tasks within the chain get canceled // as that can cause premature starting of the next task in the chain. bool forConcurrentUse = forcePrepareConcurrent || !this.service.IsWriteLockHeld; - var finalState = forConcurrentUse ? ResourceState.Concurrent : ResourceState.Exclusive; + AsyncReaderWriterResourceLock.Helper.ResourceState finalState = forConcurrentUse ? ResourceState.Concurrent : ResourceState.Exclusive; object stateObject = forConcurrentUse ? (object)resource : Tuple.Create(resource, this.service.GetAggregateLockFlags()); if (!this.resourcePreparationTasks.TryGetValue(resource, out ResourcePreparationTaskAndValidity preparationTask)) { - var preparationDelegate = forConcurrentUse + Func? preparationDelegate = forConcurrentUse ? this.prepareResourceConcurrentDelegate : this.prepareResourceExclusiveDelegate; @@ -746,7 +749,7 @@ namespace Microsoft.VisualStudio.Threading } else if (preparationTask.State != finalState || preparationTask.PreparationTask.IsFaulted) { - var preparationDelegate = forConcurrentUse + Func? preparationDelegate = forConcurrentUse ? this.prepareResourceConcurrentContinuationDelegate : this.prepareResourceExclusiveContinuationDelegate; @@ -782,7 +785,7 @@ namespace Microsoft.VisualStudio.Threading Verify.FailOperation(Strings.InvalidWithoutLock); } - var awaiter = this.service.ReadLockAsync(CancellationToken.None).GetAwaiter(); + AsyncReaderWriterResourceLock.ResourceAwaiter awaiter = this.service.ReadLockAsync(CancellationToken.None).GetAwaiter(); Assumes.True(awaiter.IsCompleted); return awaiter.GetResult(); } diff --git a/src/Microsoft.VisualStudio.Threading/AsyncSemaphore.cs b/src/Microsoft.VisualStudio.Threading/AsyncSemaphore.cs index 84fa8119..f92cdc0f 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncSemaphore.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncSemaphore.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -123,7 +120,7 @@ namespace Microsoft.VisualStudio.Threading else { WaiterInfo info = new WaiterInfo(this, cancellationToken); - var node = this.GetNode(info); + LinkedListNode? node = this.GetNode(info); // Careful: consider that if the token was cancelled just now (after we checked it on entry to this method) // or the timeout expires, @@ -204,7 +201,7 @@ namespace Microsoft.VisualStudio.Threading if (waitersCopy is object) { - foreach (var waitInfo in waitersCopy) + foreach (WaiterInfo? waitInfo in waitersCopy) { waitInfo.Cleanup(); } @@ -212,6 +209,29 @@ namespace Microsoft.VisualStudio.Threading } } + private static void CancellationHandler(object? state) + { + var waiterInfo = (WaiterInfo)state!; + + // The party that manages to complete or cancel the task is responsible to remove it from the queue. + if (waiterInfo.Trigger.TrySetCanceled(waiterInfo.CancellationToken.IsCancellationRequested ? waiterInfo.CancellationToken : new CancellationToken(true))) + { + // If the node is in the queue, remove it. + // It might not have been added yet if cancellation was already requested by the time we called Register. + lock (waiterInfo.Owner.syncObject) + { + if (waiterInfo.Node is { } node) + { + waiterInfo.Owner.waiters.Remove(node); + waiterInfo.Owner.RecycleNode(node); + } + } + } + + // Clear registration and references. + waiterInfo.Cleanup(); + } + private void Release() { WaiterInfo? info = null; @@ -243,11 +263,38 @@ namespace Microsoft.VisualStudio.Threading info?.Cleanup(); } + private void RecycleNode(LinkedListNode node) + { + Assumes.True(Monitor.IsEntered(this.syncObject)); + node.Value.Node = null; + if (this.nodePool.Count < 10) + { + LinkedListNode nullableNode = node!; + nullableNode.Value = null; + this.nodePool.Push(nullableNode); + } + } + + private LinkedListNode GetNode(WaiterInfo info) + { + Assumes.True(Monitor.IsEntered(this.syncObject)); + if (this.nodePool.Count > 0) + { + LinkedListNode? node = this.nodePool.Pop(); + node.Value = info; + return node!; + } + + return new LinkedListNode(info); + } + /// /// A value whose disposal triggers the release of a lock. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct Releaser : IDisposable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The lock instance to release. @@ -268,61 +315,13 @@ namespace Microsoft.VisualStudio.Threading /// public void Dispose() { - if (this.toRelease != null) + if (this.toRelease is object) { this.toRelease.Release(); } } } - private static void CancellationHandler(object? state) - { - var waiterInfo = (WaiterInfo)state!; - - // The party that manages to complete or cancel the task is responsible to remove it from the queue. - if (waiterInfo.Trigger.TrySetCanceled(waiterInfo.CancellationToken.IsCancellationRequested ? waiterInfo.CancellationToken : new CancellationToken(true))) - { - // If the node is in the queue, remove it. - // It might not have been added yet if cancellation was already requested by the time we called Register. - lock (waiterInfo.Owner.syncObject) - { - if (waiterInfo.Node is { } node) - { - waiterInfo.Owner.waiters.Remove(node); - waiterInfo.Owner.RecycleNode(node); - } - } - } - - // Clear registration and references. - waiterInfo.Cleanup(); - } - - private void RecycleNode(LinkedListNode node) - { - Assumes.True(Monitor.IsEntered(this.syncObject)); - node.Value.Node = null; - if (this.nodePool.Count < 10) - { - LinkedListNode nullableNode = node!; - nullableNode.Value = null; - this.nodePool.Push(nullableNode); - } - } - - private LinkedListNode GetNode(WaiterInfo info) - { - Assumes.True(Monitor.IsEntered(this.syncObject)); - if (this.nodePool.Count > 0) - { - var node = this.nodePool.Pop(); - node.Value = info; - return node!; - } - - return new LinkedListNode(info); - } - private class WaiterInfo { internal WaiterInfo(AsyncSemaphore owner, CancellationToken cancellationToken) diff --git a/src/Microsoft.VisualStudio.Threading/AwaitExtensions.cs b/src/Microsoft.VisualStudio.Threading/AwaitExtensions.cs index 9561adff..e5b6b9de 100644 --- a/src/Microsoft.VisualStudio.Threading/AwaitExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading/AwaitExtensions.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -116,6 +113,53 @@ namespace Microsoft.VisualStudio.Threading return WaitForRegistryChangeAsync(registryKey.Handle, watchSubtree, change, cancellationToken); } + /// + /// Converts a to a . + /// + /// The result of . + /// A value indicating whether the continuation should run on the captured , if any. + /// An awaitable. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "yieldAwaitable", Justification = "This allows the extension method syntax to work.")] + public static ConfiguredTaskYieldAwaitable ConfigureAwait(this YieldAwaitable yieldAwaitable, bool continueOnCapturedContext) + { + return new ConfiguredTaskYieldAwaitable(continueOnCapturedContext); + } + + /// + /// Gets an awaitable that schedules the continuation with a preference to executing synchronously on the callstack that completed the , + /// without regard to thread ID or any that may be applied when the continuation is scheduled or when the antecedent completes. + /// + /// The task to await on. + /// An awaitable. + /// + /// If there is not enough stack space remaining on the thread that is completing the , + /// the continuation may be scheduled on the threadpool. + /// + public static ExecuteContinuationSynchronouslyAwaitable ConfigureAwaitRunInline(this Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + + return new ExecuteContinuationSynchronouslyAwaitable(antecedent); + } + + /// + /// Gets an awaitable that schedules the continuation with a preference to executing synchronously on the callstack that completed the , + /// without regard to thread ID or any that may be applied when the continuation is scheduled or when the antecedent completes. + /// + /// The type of value returned by the awaited . + /// The task to await on. + /// An awaitable. + /// + /// If there is not enough stack space remaining on the thread that is completing the , + /// the continuation may be scheduled on the threadpool. + /// + public static ExecuteContinuationSynchronouslyAwaitable ConfigureAwaitRunInline(this Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + + return new ExecuteContinuationSynchronouslyAwaitable(antecedent); + } + /// /// Returns a Task that completes when the specified registry key changes. /// @@ -174,6 +218,414 @@ namespace Microsoft.VisualStudio.Threading } } + /// + /// An awaitable that executes continuations on the specified task scheduler. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct TaskSchedulerAwaitable +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The scheduler for continuations. + /// + private readonly TaskScheduler taskScheduler; + + /// + /// A value indicating whether the awaitable will always call the caller to yield. + /// + private readonly bool alwaysYield; + + /// + /// Initializes a new instance of the struct. + /// + /// The task scheduler used to execute continuations. + /// A value indicating whether the caller should yield even if + /// already executing on the desired task scheduler. + public TaskSchedulerAwaitable(TaskScheduler taskScheduler, bool alwaysYield = false) + { + Requires.NotNull(taskScheduler, nameof(taskScheduler)); + + this.taskScheduler = taskScheduler; + this.alwaysYield = alwaysYield; + } + + /// + /// Gets an awaitable that schedules continuations on the specified scheduler. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public TaskSchedulerAwaiter GetAwaiter() + { + return new TaskSchedulerAwaiter(this.taskScheduler, this.alwaysYield); + } + } + + /// + /// An awaiter returned from . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct TaskSchedulerAwaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The scheduler for continuations. + /// + private readonly TaskScheduler scheduler; + + /// + /// A value indicating whether + /// should always return false. + /// + private readonly bool alwaysYield; + + /// + /// Initializes a new instance of the struct. + /// + /// The scheduler for continuations. + /// A value indicating whether the caller should yield even if + /// already executing on the desired task scheduler. + public TaskSchedulerAwaiter(TaskScheduler scheduler, bool alwaysYield = false) + { + this.scheduler = scheduler; + this.alwaysYield = alwaysYield; + } + + /// + /// Gets a value indicating whether no yield is necessary. + /// + /// true if the caller is already running on that TaskScheduler. + public bool IsCompleted + { + get + { + if (this.alwaysYield) + { + return false; + } + + // We special case the TaskScheduler.Default since that is semantically equivalent to being + // on a ThreadPool thread, and there are various ways to get on those threads. + // TaskScheduler.Current is never null. Even if no scheduler is really active and the current + // thread is not a threadpool thread, TaskScheduler.Current == TaskScheduler.Default, so we have + // to protect against that case too. + bool isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; + return (this.scheduler == TaskScheduler.Default && isThreadPoolThread) + || (this.scheduler == TaskScheduler.Current && TaskScheduler.Current != TaskScheduler.Default); + } + } + + /// + /// Schedules a continuation to execute using the specified task scheduler. + /// + /// The delegate to invoke. + public void OnCompleted(Action continuation) + { + if (this.scheduler == TaskScheduler.Default) + { + ThreadPool.QueueUserWorkItem(state => ((Action)state!)(), continuation); + } + else + { + Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.None, this.scheduler); + } + } + + /// + /// Schedules a continuation to execute using the specified task scheduler + /// without capturing the ExecutionContext. + /// + /// The action. + public void UnsafeOnCompleted(Action continuation) + { + if (this.scheduler == TaskScheduler.Default) + { + ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state!)(), continuation); + } + else + { + // There is no API for scheduling a Task without capturing the ExecutionContext. + Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.None, this.scheduler); + } + } + + /// + /// Does nothing. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public void GetResult() + { + } + } + + /// + /// An awaitable that will always lead the calling async method to yield, + /// then immediately resume, possibly on the original . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ConfiguredTaskYieldAwaitable +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// A value indicating whether the continuation should run on the captured , if any. + /// + private readonly bool continueOnCapturedContext; + + /// + /// Initializes a new instance of the struct. + /// + /// A value indicating whether the continuation should run on the captured , if any. + public ConfiguredTaskYieldAwaitable(bool continueOnCapturedContext) + { + this.continueOnCapturedContext = continueOnCapturedContext; + } + + /// + /// Gets the awaiter. + /// + /// The awaiter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ConfiguredTaskYieldAwaiter GetAwaiter() => new ConfiguredTaskYieldAwaiter(this.continueOnCapturedContext); + } + + /// + /// An awaiter that will always lead the calling async method to yield, + /// then immediately resume, possibly on the original . + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ConfiguredTaskYieldAwaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// A value indicating whether the continuation should run on the captured , if any. + /// + private readonly bool continueOnCapturedContext; + + /// + /// Initializes a new instance of the struct. + /// + /// A value indicating whether the continuation should run on the captured , if any. + public ConfiguredTaskYieldAwaiter(bool continueOnCapturedContext) + { + this.continueOnCapturedContext = continueOnCapturedContext; + } + + /// + /// Gets a value indicating whether the caller should yield. + /// + /// Always false. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public bool IsCompleted => false; + + /// + /// Schedules a continuation to execute immediately (but not synchronously). + /// + /// The delegate to invoke. + public void OnCompleted(Action continuation) + { + if (this.continueOnCapturedContext) + { + Task.Yield().GetAwaiter().OnCompleted(continuation); + } + else + { + ThreadPool.QueueUserWorkItem(state => ((Action)state!)(), continuation); + } + } + + /// + /// Schedules a delegate for execution at the conclusion of a task's execution + /// without capturing the ExecutionContext. + /// + /// The action. + public void UnsafeOnCompleted(Action continuation) + { + if (this.continueOnCapturedContext) + { + Task.Yield().GetAwaiter().UnsafeOnCompleted(continuation); + } + else + { + ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state!)(), continuation); + } + } + + /// + /// Does nothing. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public void GetResult() + { + } + } + + /// + /// A Task awaitable that has affinity to executing callbacks synchronously on the completing callstack. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ExecuteContinuationSynchronouslyAwaitable +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The task whose completion will execute the continuation. + /// + private readonly Task antecedent; + + /// + /// Initializes a new instance of the struct. + /// + /// The task whose completion will execute the continuation. + public ExecuteContinuationSynchronouslyAwaitable(Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + this.antecedent = antecedent; + } + + /// + /// Gets the awaiter. + /// + /// The awaiter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ExecuteContinuationSynchronouslyAwaiter GetAwaiter() => new ExecuteContinuationSynchronouslyAwaiter(this.antecedent); + } + + /// + /// A Task awaiter that has affinity to executing callbacks synchronously on the completing callstack. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ExecuteContinuationSynchronouslyAwaiter : INotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The task whose completion will execute the continuation. + /// + private readonly Task antecedent; + + /// + /// Initializes a new instance of the struct. + /// + /// The task whose completion will execute the continuation. + public ExecuteContinuationSynchronouslyAwaiter(Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + this.antecedent = antecedent; + } + + /// + /// Gets a value indicating whether the antedent has already completed. + /// + public bool IsCompleted => this.antecedent.IsCompleted; + + /// + /// Rethrows any exception thrown by the antecedent. + /// + public void GetResult() => this.antecedent.GetAwaiter().GetResult(); + + /// + /// Schedules a callback to run when the antecedent task completes. + /// + /// The callback to invoke. + public void OnCompleted(Action continuation) + { + Requires.NotNull(continuation, nameof(continuation)); + + this.antecedent.ContinueWith( + (_, s) => ((Action)s!)(), + continuation, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + } + + /// + /// A Task awaitable that has affinity to executing callbacks synchronously on the completing callstack. + /// + /// The type of value returned by the awaited . + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ExecuteContinuationSynchronouslyAwaitable +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The task whose completion will execute the continuation. + /// + private readonly Task antecedent; + + /// + /// Initializes a new instance of the struct. + /// + /// The task whose completion will execute the continuation. + public ExecuteContinuationSynchronouslyAwaitable(Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + this.antecedent = antecedent; + } + + /// + /// Gets the awaiter. + /// + /// The awaiter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public ExecuteContinuationSynchronouslyAwaiter GetAwaiter() => new ExecuteContinuationSynchronouslyAwaiter(this.antecedent); + } + + /// + /// A Task awaiter that has affinity to executing callbacks synchronously on the completing callstack. + /// + /// The type of value returned by the awaited . + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible + public readonly struct ExecuteContinuationSynchronouslyAwaiter : INotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible + { + /// + /// The task whose completion will execute the continuation. + /// + private readonly Task antecedent; + + /// + /// Initializes a new instance of the struct. + /// + /// The task whose completion will execute the continuation. + public ExecuteContinuationSynchronouslyAwaiter(Task antecedent) + { + Requires.NotNull(antecedent, nameof(antecedent)); + this.antecedent = antecedent; + } + + /// + /// Gets a value indicating whether the antedent has already completed. + /// + public bool IsCompleted => this.antecedent.IsCompleted; + + /// + /// Rethrows any exception thrown by the antecedent. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public T GetResult() => this.antecedent.GetAwaiter().GetResult(); + + /// + /// Schedules a callback to run when the antecedent task completes. + /// + /// The callback to invoke. + public void OnCompleted(Action continuation) + { + Requires.NotNull(continuation, nameof(continuation)); + + this.antecedent.ContinueWith( + (_, s) => ((Action)s!)(), + continuation, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + } + /// /// Provides a dedicated thread for requesting registry change notifications. /// @@ -330,7 +782,7 @@ namespace Microsoft.VisualStudio.Threading // This happens when keepAliveCount (at least temporarily) // hits 0, so this thread must be assumed to be on its exit path, // and another thread will be spawned to process new requests. - Assumes.True(liveThread != null || (keepAliveCount == 0 && PendingWork.Count == 0)); + Assumes.True(liveThread is object || (keepAliveCount == 0 && PendingWork.Count == 0)); return; } @@ -350,7 +802,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (work != null) + if (work is object) { try { @@ -391,444 +843,5 @@ namespace Microsoft.VisualStudio.Threading } } } - - /// - /// Converts a to a . - /// - /// The result of . - /// A value indicating whether the continuation should run on the captured , if any. - /// An awaitable. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "yieldAwaitable", Justification = "This allows the extension method syntax to work.")] - public static ConfiguredTaskYieldAwaitable ConfigureAwait(this YieldAwaitable yieldAwaitable, bool continueOnCapturedContext) - { - return new ConfiguredTaskYieldAwaitable(continueOnCapturedContext); - } - - /// - /// Gets an awaitable that schedules the continuation with a preference to executing synchronously on the callstack that completed the , - /// without regard to thread ID or any that may be applied when the continuation is scheduled or when the antecedent completes. - /// - /// The task to await on. - /// An awaitable. - /// - /// If there is not enough stack space remaining on the thread that is completing the , - /// the continuation may be scheduled on the threadpool. - /// - public static ExecuteContinuationSynchronouslyAwaitable ConfigureAwaitRunInline(this Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - - return new ExecuteContinuationSynchronouslyAwaitable(antecedent); - } - - /// - /// Gets an awaitable that schedules the continuation with a preference to executing synchronously on the callstack that completed the , - /// without regard to thread ID or any that may be applied when the continuation is scheduled or when the antecedent completes. - /// - /// The type of value returned by the awaited . - /// The task to await on. - /// An awaitable. - /// - /// If there is not enough stack space remaining on the thread that is completing the , - /// the continuation may be scheduled on the threadpool. - /// - public static ExecuteContinuationSynchronouslyAwaitable ConfigureAwaitRunInline(this Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - - return new ExecuteContinuationSynchronouslyAwaitable(antecedent); - } - - /// - /// An awaitable that executes continuations on the specified task scheduler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct TaskSchedulerAwaitable - { - /// - /// The scheduler for continuations. - /// - private readonly TaskScheduler taskScheduler; - - /// - /// A value indicating whether the awaitable will always call the caller to yield. - /// - private readonly bool alwaysYield; - - /// - /// Initializes a new instance of the struct. - /// - /// The task scheduler used to execute continuations. - /// A value indicating whether the caller should yield even if - /// already executing on the desired task scheduler. - public TaskSchedulerAwaitable(TaskScheduler taskScheduler, bool alwaysYield = false) - { - Requires.NotNull(taskScheduler, nameof(taskScheduler)); - - this.taskScheduler = taskScheduler; - this.alwaysYield = alwaysYield; - } - - /// - /// Gets an awaitable that schedules continuations on the specified scheduler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public TaskSchedulerAwaiter GetAwaiter() - { - return new TaskSchedulerAwaiter(this.taskScheduler, this.alwaysYield); - } - } - - /// - /// An awaiter returned from . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct TaskSchedulerAwaiter : ICriticalNotifyCompletion - { - /// - /// The scheduler for continuations. - /// - private readonly TaskScheduler scheduler; - - /// - /// A value indicating whether - /// should always return false. - /// - private readonly bool alwaysYield; - - /// - /// Initializes a new instance of the struct. - /// - /// The scheduler for continuations. - /// A value indicating whether the caller should yield even if - /// already executing on the desired task scheduler. - public TaskSchedulerAwaiter(TaskScheduler scheduler, bool alwaysYield = false) - { - this.scheduler = scheduler; - this.alwaysYield = alwaysYield; - } - - /// - /// Gets a value indicating whether no yield is necessary. - /// - /// true if the caller is already running on that TaskScheduler. - public bool IsCompleted - { - get - { - if (this.alwaysYield) - { - return false; - } - - // We special case the TaskScheduler.Default since that is semantically equivalent to being - // on a ThreadPool thread, and there are various ways to get on those threads. - // TaskScheduler.Current is never null. Even if no scheduler is really active and the current - // thread is not a threadpool thread, TaskScheduler.Current == TaskScheduler.Default, so we have - // to protect against that case too. - bool isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; - return (this.scheduler == TaskScheduler.Default && isThreadPoolThread) - || (this.scheduler == TaskScheduler.Current && TaskScheduler.Current != TaskScheduler.Default); - } - } - - /// - /// Schedules a continuation to execute using the specified task scheduler. - /// - /// The delegate to invoke. - public void OnCompleted(Action continuation) - { - if (this.scheduler == TaskScheduler.Default) - { - ThreadPool.QueueUserWorkItem(state => ((Action)state!)(), continuation); - } - else - { - Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.None, this.scheduler); - } - } - - /// - /// Schedules a continuation to execute using the specified task scheduler - /// without capturing the ExecutionContext. - /// - /// The action. - public void UnsafeOnCompleted(Action continuation) - { - if (this.scheduler == TaskScheduler.Default) - { - ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state!)(), continuation); - } - else - { - // There is no API for scheduling a Task without capturing the ExecutionContext. - Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.None, this.scheduler); - } - } - - /// - /// Does nothing. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] - public void GetResult() - { - } - } - - /// - /// An awaitable that will always lead the calling async method to yield, - /// then immediately resume, possibly on the original . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ConfiguredTaskYieldAwaitable - { - /// - /// A value indicating whether the continuation should run on the captured , if any. - /// - private readonly bool continueOnCapturedContext; - - /// - /// Initializes a new instance of the struct. - /// - /// A value indicating whether the continuation should run on the captured , if any. - public ConfiguredTaskYieldAwaitable(bool continueOnCapturedContext) - { - this.continueOnCapturedContext = continueOnCapturedContext; - } - - /// - /// Gets the awaiter. - /// - /// The awaiter. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public ConfiguredTaskYieldAwaiter GetAwaiter() => new ConfiguredTaskYieldAwaiter(this.continueOnCapturedContext); - } - - /// - /// An awaiter that will always lead the calling async method to yield, - /// then immediately resume, possibly on the original . - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ConfiguredTaskYieldAwaiter : ICriticalNotifyCompletion - { - /// - /// A value indicating whether the continuation should run on the captured , if any. - /// - private readonly bool continueOnCapturedContext; - - /// - /// Initializes a new instance of the struct. - /// - /// A value indicating whether the continuation should run on the captured , if any. - public ConfiguredTaskYieldAwaiter(bool continueOnCapturedContext) - { - this.continueOnCapturedContext = continueOnCapturedContext; - } - - /// - /// Gets a value indicating whether the caller should yield. - /// - /// Always false. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] - public bool IsCompleted => false; - - /// - /// Schedules a continuation to execute immediately (but not synchronously). - /// - /// The delegate to invoke. - public void OnCompleted(Action continuation) - { - if (this.continueOnCapturedContext) - { - Task.Yield().GetAwaiter().OnCompleted(continuation); - } - else - { - ThreadPool.QueueUserWorkItem(state => ((Action)state!)(), continuation); - } - } - - /// - /// Schedules a delegate for execution at the conclusion of a task's execution - /// without capturing the ExecutionContext. - /// - /// The action. - public void UnsafeOnCompleted(Action continuation) - { - if (this.continueOnCapturedContext) - { - Task.Yield().GetAwaiter().UnsafeOnCompleted(continuation); - } - else - { - ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state!)(), continuation); - } - } - - /// - /// Does nothing. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] - public void GetResult() - { - } - } - - /// - /// A Task awaitable that has affinity to executing callbacks synchronously on the completing callstack. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ExecuteContinuationSynchronouslyAwaitable - { - /// - /// The task whose completion will execute the continuation. - /// - private readonly Task antecedent; - - /// - /// Initializes a new instance of the struct. - /// - /// The task whose completion will execute the continuation. - public ExecuteContinuationSynchronouslyAwaitable(Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - this.antecedent = antecedent; - } - - /// - /// Gets the awaiter. - /// - /// The awaiter. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public ExecuteContinuationSynchronouslyAwaiter GetAwaiter() => new ExecuteContinuationSynchronouslyAwaiter(this.antecedent); - } - - /// - /// A Task awaiter that has affinity to executing callbacks synchronously on the completing callstack. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ExecuteContinuationSynchronouslyAwaiter : INotifyCompletion - { - /// - /// The task whose completion will execute the continuation. - /// - private readonly Task antecedent; - - /// - /// Initializes a new instance of the struct. - /// - /// The task whose completion will execute the continuation. - public ExecuteContinuationSynchronouslyAwaiter(Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - this.antecedent = antecedent; - } - - /// - /// Gets a value indicating whether the antedent has already completed. - /// - public bool IsCompleted => this.antecedent.IsCompleted; - - /// - /// Rethrows any exception thrown by the antecedent. - /// - public void GetResult() => this.antecedent.GetAwaiter().GetResult(); - - /// - /// Schedules a callback to run when the antecedent task completes. - /// - /// The callback to invoke. - public void OnCompleted(Action continuation) - { - Requires.NotNull(continuation, nameof(continuation)); - - this.antecedent.ContinueWith( - (_, s) => ((Action)s!)(), - continuation, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - } - - /// - /// A Task awaitable that has affinity to executing callbacks synchronously on the completing callstack. - /// - /// The type of value returned by the awaited . - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ExecuteContinuationSynchronouslyAwaitable - { - /// - /// The task whose completion will execute the continuation. - /// - private readonly Task antecedent; - - /// - /// Initializes a new instance of the struct. - /// - /// The task whose completion will execute the continuation. - public ExecuteContinuationSynchronouslyAwaitable(Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - this.antecedent = antecedent; - } - - /// - /// Gets the awaiter. - /// - /// The awaiter. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public ExecuteContinuationSynchronouslyAwaiter GetAwaiter() => new ExecuteContinuationSynchronouslyAwaiter(this.antecedent); - } - - /// - /// A Task awaiter that has affinity to executing callbacks synchronously on the completing callstack. - /// - /// The type of value returned by the awaited . - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] - public readonly struct ExecuteContinuationSynchronouslyAwaiter : INotifyCompletion - { - /// - /// The task whose completion will execute the continuation. - /// - private readonly Task antecedent; - - /// - /// Initializes a new instance of the struct. - /// - /// The task whose completion will execute the continuation. - public ExecuteContinuationSynchronouslyAwaiter(Task antecedent) - { - Requires.NotNull(antecedent, nameof(antecedent)); - this.antecedent = antecedent; - } - - /// - /// Gets a value indicating whether the antedent has already completed. - /// - public bool IsCompleted => this.antecedent.IsCompleted; - - /// - /// Rethrows any exception thrown by the antecedent. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public T GetResult() => this.antecedent.GetAwaiter().GetResult(); - - /// - /// Schedules a callback to run when the antecedent task completes. - /// - /// The callback to invoke. - public void OnCompleted(Action continuation) - { - Requires.NotNull(continuation, nameof(continuation)); - - this.antecedent.ContinueWith( - (_, s) => ((Action)s!)(), - continuation, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - } } } diff --git a/src/Microsoft.VisualStudio.Threading/CancellationTokenExtensions.cs b/src/Microsoft.VisualStudio.Threading/CancellationTokenExtensions.cs index cd251df9..aa880a7f 100644 --- a/src/Microsoft.VisualStudio.Threading/CancellationTokenExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading/CancellationTokenExtensions.cs @@ -1,4 +1,5 @@ -// Copyright (c) PlaceholderCompany. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -51,7 +52,7 @@ namespace Microsoft.VisualStudio.Threading } int cancelableTokensCount = original.CanBeCanceled ? 1 : 0; - foreach (var other in others) + foreach (CancellationToken other in others) { if (other.IsCancellationRequested) { @@ -74,7 +75,7 @@ namespace Microsoft.VisualStudio.Threading return new CombinedCancellationToken(original); } - foreach (var other in others) + foreach (CancellationToken other in others) { if (other.CanBeCanceled) { @@ -92,7 +93,7 @@ namespace Microsoft.VisualStudio.Threading first = original; } - foreach (var other in others) + foreach (CancellationToken other in others) { if (other.CanBeCanceled) { @@ -116,7 +117,7 @@ namespace Microsoft.VisualStudio.Threading // Before this point we've checked every condition that would allow us to avoid it. var cancelableTokens = new CancellationToken[cancelableTokensCount]; int i = 0; - foreach (var other in others) + foreach (CancellationToken other in others) { if (other.CanBeCanceled) { @@ -132,7 +133,9 @@ namespace Microsoft.VisualStudio.Threading /// Provides access to a that combines multiple other tokens, /// and allows convenient disposal of any applicable . /// +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct CombinedCancellationToken : IDisposable, IEquatable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The object to dispose when this struct is disposed. @@ -162,6 +165,11 @@ namespace Microsoft.VisualStudio.Threading this.Token = cancellationToken; } + /// + /// Gets the combined cancellation token. + /// + public CancellationToken Token { get; } + /// /// Checks whether two instances of are equal. /// @@ -178,11 +186,6 @@ namespace Microsoft.VisualStudio.Threading /// true if they are not equal; false if they are equal. public static bool operator !=(CombinedCancellationToken left, CombinedCancellationToken right) => !(left == right); - /// - /// Gets the combined cancellation token. - /// - public CancellationToken Token { get; } - /// /// Disposes the behind this combined token, if any. /// diff --git a/src/Microsoft.VisualStudio.Threading/DelegatingJoinableTaskFactory.cs b/src/Microsoft.VisualStudio.Threading/DelegatingJoinableTaskFactory.cs index ce537e9c..d19af179 100644 --- a/src/Microsoft.VisualStudio.Threading/DelegatingJoinableTaskFactory.cs +++ b/src/Microsoft.VisualStudio.Threading/DelegatingJoinableTaskFactory.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/Dgml.cs b/src/Microsoft.VisualStudio.Threading/Dgml.cs index a880af80..390ed814 100644 --- a/src/Microsoft.VisualStudio.Threading/Dgml.cs +++ b/src/Microsoft.VisualStudio.Threading/Dgml.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -31,9 +28,10 @@ namespace Microsoft.VisualStudio.Threading { var dgml = new XDocument(); dgml.Add( - new XElement(XName.Get("DirectedGraph", Namespace), + new XElement( + XName.Get("DirectedGraph", Namespace), new XAttribute("Layout", layout))); - if (direction != null) + if (direction is object) { dgml.Root.Add(new XAttribute("GraphDirection", direction)); } @@ -46,28 +44,6 @@ namespace Microsoft.VisualStudio.Threading return dgml; } - private static XElement GetRootElement(this XDocument document, XName name) - { - Requires.NotNull(document, nameof(document)); - Requires.NotNull(name, nameof(name)); - - var container = document.Root.Element(name); - if (container == null) - { - document.Root.Add(container = new XElement(name)); - } - - return container; - } - - private static XElement GetRootElement(XDocument document, string elementName) - { - Requires.NotNull(document, nameof(document)); - Requires.NotNullOrEmpty(elementName, nameof(elementName)); - - return GetRootElement(document, XName.Get(elementName, Namespace)); - } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal static XDocument WithCategories(this XDocument document, params string[] categories) { @@ -114,7 +90,7 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(document, nameof(document)); Requires.NotNull(node, nameof(node)); - var nodes = document.GetRootElement(NodesName); + XElement? nodes = document.GetRootElement(NodesName); nodes.Add(node); return document; } @@ -140,7 +116,7 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(document, nameof(document)); Requires.NotNull(link, nameof(link)); - var links = document.GetRootElement(LinksName); + XElement? links = document.GetRootElement(LinksName); links.Add(link); return document; } @@ -195,7 +171,7 @@ namespace Microsoft.VisualStudio.Threading internal static XDocument WithContainers(this XDocument document, IEnumerable containers) { - foreach (var container in containers) + foreach (XElement? container in containers) { WithNode(document, container); } @@ -234,7 +210,7 @@ namespace Microsoft.VisualStudio.Threading foreach (var category in categories) { - if (element.Attribute("Category") == null) + if (element.Attribute("Category") is null) { element.SetAttributeValue("Category", category); } @@ -257,13 +233,14 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(properties, nameof(properties)); Requires.NotNullOrEmpty(targetType, nameof(targetType)); - var container = document.Root.Element(StylesName); - if (container == null) + XElement? container = document.Root.Element(StylesName); + if (container is null) { document.Root.Add(container = new XElement(StylesName)); } - var style = new XElement(StyleName, + var style = new XElement( + StyleName, new XAttribute("TargetType", targetType), new XAttribute("GroupLabel", categoryId), new XElement(XName.Get("Condition", Namespace), new XAttribute("Expression", "HasCategory('" + categoryId + "')"))); @@ -295,5 +272,27 @@ namespace Microsoft.VisualStudio.Threading return WithStyle(document, categoryId, properties, targetType); } + + private static XElement GetRootElement(this XDocument document, XName name) + { + Requires.NotNull(document, nameof(document)); + Requires.NotNull(name, nameof(name)); + + XElement? container = document.Root.Element(name); + if (container is null) + { + document.Root.Add(container = new XElement(name)); + } + + return container; + } + + private static XElement GetRootElement(XDocument document, string elementName) + { + Requires.NotNull(document, nameof(document)); + Requires.NotNullOrEmpty(elementName, nameof(elementName)); + + return GetRootElement(document, XName.Get(elementName, Namespace)); + } } } diff --git a/src/Microsoft.VisualStudio.Threading/DispatcherExtensions.cs b/src/Microsoft.VisualStudio.Threading/DispatcherExtensions.cs index 3d55f085..682f6075 100644 --- a/src/Microsoft.VisualStudio.Threading/DispatcherExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading/DispatcherExtensions.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETFRAMEWORK diff --git a/src/Microsoft.VisualStudio.Threading/EmptyStruct.cs b/src/Microsoft.VisualStudio.Threading/EmptyStruct.cs index 3efeddf1..2e16ca38 100644 --- a/src/Microsoft.VisualStudio.Threading/EmptyStruct.cs +++ b/src/Microsoft.VisualStudio.Threading/EmptyStruct.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany.cs b/src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany`1.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany.cs rename to src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany`1.cs index 3d12b6db..bd898c3b 100644 --- a/src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany.cs +++ b/src/Microsoft.VisualStudio.Threading/EnumerateOneOrMany`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/FxCopRules.ruleset b/src/Microsoft.VisualStudio.Threading/FxCopRules.ruleset deleted file mode 100644 index a3e884c9..00000000 --- a/src/Microsoft.VisualStudio.Threading/FxCopRules.ruleset +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs b/src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs index ca944a64..ad548396 100644 Binary files a/src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs and b/src/Microsoft.VisualStudio.Threading/GlobalSuppressions.cs differ diff --git a/src/Microsoft.VisualStudio.Threading/HangReportContribution.cs b/src/Microsoft.VisualStudio.Threading/HangReportContribution.cs index 7d17fcfa..5a7ee302 100644 --- a/src/Microsoft.VisualStudio.Threading/HangReportContribution.cs +++ b/src/Microsoft.VisualStudio.Threading/HangReportContribution.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/IAsyncDisposable.cs b/src/Microsoft.VisualStudio.Threading/IAsyncDisposable.cs index e57c5e37..867c595e 100644 --- a/src/Microsoft.VisualStudio.Threading/IAsyncDisposable.cs +++ b/src/Microsoft.VisualStudio.Threading/IAsyncDisposable.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/IHangReportContributor.cs b/src/Microsoft.VisualStudio.Threading/IHangReportContributor.cs index 9ed29895..ab271287 100644 --- a/src/Microsoft.VisualStudio.Threading/IHangReportContributor.cs +++ b/src/Microsoft.VisualStudio.Threading/IHangReportContributor.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/IJoinableTaskDependent.cs b/src/Microsoft.VisualStudio.Threading/IJoinableTaskDependent.cs index f79a6054..dc80229c 100644 --- a/src/Microsoft.VisualStudio.Threading/IJoinableTaskDependent.cs +++ b/src/Microsoft.VisualStudio.Threading/IJoinableTaskDependent.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/InlineResumable.cs b/src/Microsoft.VisualStudio.Threading/InlineResumable.cs index a6e00e02..f38e068b 100644 --- a/src/Microsoft.VisualStudio.Threading/InlineResumable.cs +++ b/src/Microsoft.VisualStudio.Threading/InlineResumable.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -83,7 +80,7 @@ namespace Microsoft.VisualStudio.Threading public void Resume() { this.resumed = true; - var continuation = this.continuation; + Action? continuation = this.continuation; this.continuation = null; using (this.capturedSynchronizationContext.Apply()) { diff --git a/src/Microsoft.VisualStudio.Threading/InternalUtilities.cs b/src/Microsoft.VisualStudio.Threading/InternalUtilities.cs index 68cec828..112cca21 100644 --- a/src/Microsoft.VisualStudio.Threading/InternalUtilities.cs +++ b/src/Microsoft.VisualStudio.Threading/InternalUtilities.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -71,8 +68,8 @@ namespace Microsoft.VisualStudio.Threading /// The delegate that represents the head of an async continuation chain. internal static IEnumerable GetAsyncReturnStackFrames(this Delegate continuationDelegate) { - var stateMachine = FindAsyncStateMachine(continuationDelegate); - if (stateMachine == null) + IAsyncStateMachine? stateMachine = FindAsyncStateMachine(continuationDelegate); + if (stateMachine is null) { // Did not find the async state machine, so returns the method name as top frame and stop walking. yield return GetDelegateLabel(continuationDelegate); @@ -90,7 +87,7 @@ namespace Microsoft.VisualStudio.Threading AsyncReturnStackPrefix, (int)GetAddress(stateMachine)); // the int cast allows hex formatting - var continuationDelegates = FindContinuationDelegates(stateMachine).ToArray(); + Delegate[]? continuationDelegates = FindContinuationDelegates(stateMachine).ToArray(); if (continuationDelegates.Length == 0) { break; @@ -100,12 +97,13 @@ namespace Microsoft.VisualStudio.Threading // Here we just choose the first awaiting "async method" as that should be good enough for postmortem. // In future we might want to revisit this to cover the other awaiting "async methods". stateMachine = continuationDelegates.Select((d) => FindAsyncStateMachine(d)) - .FirstOrDefault((s) => s != null); - if (stateMachine == null) + .FirstOrDefault((s) => s is object); + if (stateMachine is null) { yield return GetDelegateLabel(continuationDelegates.First()); } - } while (stateMachine != null); + } + while (stateMachine is object); } /// @@ -116,7 +114,7 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(invokeDelegate, nameof(invokeDelegate)); MethodInfo? method = invokeDelegate.GetMethodInfo(); - if (invokeDelegate.Target != null) + if (invokeDelegate.Target is object) { string instanceType = string.Empty; if (!(method?.DeclaringType?.Equals(invokeDelegate.Target.GetType()) ?? false)) @@ -162,7 +160,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(invokeDelegate, nameof(invokeDelegate)); - if (invokeDelegate.Target != null) + if (invokeDelegate.Target is object) { // Some delegates are wrapped with a ContinuationWrapper object. We have to unwrap that in those cases. // In testing, this m_continuation field jump is only required when the debugger is attached -- weird. @@ -171,7 +169,7 @@ namespace Microsoft.VisualStudio.Threading if (GetFieldValue(invokeDelegate.Target, "m_continuation") is Action continuation) { invokeDelegate = continuation; - if (invokeDelegate.Target == null) + if (invokeDelegate.Target is null) { return null; } @@ -198,37 +196,37 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(stateMachine, nameof(stateMachine)); var builder = GetStateMachineFieldValueOnSuffix(stateMachine, "__builder"); - if (builder == null) + if (builder is null) { yield break; } var task = GetFieldValue(builder, "m_task"); - if (task == null) + if (task is null) { // Probably this builder is an instance of "AsyncTaskMethodBuilder", so we need to get its inner "AsyncTaskMethodBuilder" builder = GetFieldValue(builder, "m_builder"); - if (builder != null) + if (builder is object) { task = GetFieldValue(builder, "m_task"); } } - if (task == null) + if (task is null) { yield break; } // "task" might be an instance of the type deriving from "Task", but "m_continuationObject" is a private field in "Task", // so we need to use "typeof(Task)" to access "m_continuationObject". - var continuationField = typeof(Task).GetTypeInfo().GetDeclaredField("m_continuationObject"); - if (continuationField == null) + FieldInfo? continuationField = typeof(Task).GetTypeInfo().GetDeclaredField("m_continuationObject"); + if (continuationField is null) { yield break; } var continuationObject = continuationField.GetValue(task); - if (continuationObject == null) + if (continuationObject is null) { yield break; } @@ -237,8 +235,8 @@ namespace Microsoft.VisualStudio.Threading { foreach (var item in items) { - var action = item as Delegate ?? GetFieldValue(item!, "m_action") as Delegate; - if (action != null) + Delegate? action = item as Delegate ?? GetFieldValue(item!, "m_action") as Delegate; + if (action is object) { yield return action; } @@ -246,8 +244,8 @@ namespace Microsoft.VisualStudio.Threading } else { - var action = continuationObject as Delegate ?? GetFieldValue(continuationObject, "m_action") as Delegate; - if (action != null) + Delegate? action = continuationObject as Delegate ?? GetFieldValue(continuationObject, "m_action") as Delegate; + if (action is object) { yield return action; } @@ -262,8 +260,8 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(obj, nameof(obj)); Requires.NotNullOrEmpty(fieldName, nameof(fieldName)); - var field = obj.GetType().GetTypeInfo().GetDeclaredField(fieldName); - if (field != null) + FieldInfo? field = obj.GetType().GetTypeInfo().GetDeclaredField(fieldName); + if (field is object) { return field.GetValue(obj); } @@ -279,9 +277,9 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(stateMachine, nameof(stateMachine)); Requires.NotNullOrEmpty(suffix, nameof(suffix)); - var fields = stateMachine.GetType().GetTypeInfo().DeclaredFields; - var field = fields.FirstOrDefault((f) => f.Name.EndsWith(suffix, StringComparison.Ordinal)); - if (field != null) + IEnumerable? fields = stateMachine.GetType().GetTypeInfo().DeclaredFields; + FieldInfo? field = fields.FirstOrDefault((f) => f.Name.EndsWith(suffix, StringComparison.Ordinal)); + if (field is object) { return field.GetValue(stateMachine); } diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask+ExecutionQueue.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask+ExecutionQueue.cs index f5f68c6b..a765d3d6 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask+ExecutionQueue.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask+ExecutionQueue.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -34,6 +31,11 @@ namespace Microsoft.VisualStudio.Threading get { return 1; } // in non-concurrent cases, 1 is sufficient. } + internal void OnExecuting(object sender, EventArgs e) + { + this.Scavenge(); + } + protected override void OnEnqueued(SingleExecuteProtector value, bool alreadyDispatched) { base.OnEnqueued(value, alreadyDispatched); @@ -70,11 +72,6 @@ namespace Microsoft.VisualStudio.Threading this.owningJob.OnQueueCompleted(); } - internal void OnExecuting(object sender, EventArgs e) - { - this.Scavenge(); - } - private void Scavenge() { while (this.TryDequeue(p => p.HasBeenExecuted, out SingleExecuteProtector? stale)) diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask+JoinableTaskSynchronizationContext.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask+JoinableTaskSynchronizationContext.cs index b4210ce4..905def91 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask+JoinableTaskSynchronizationContext.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask+JoinableTaskSynchronizationContext.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -13,7 +10,7 @@ namespace Microsoft.VisualStudio.Threading using System.Threading; using System.Threading.Tasks; - partial class JoinableTask + public partial class JoinableTask { /// /// A synchronization context that forwards posted messages to the ambient job. @@ -76,7 +73,7 @@ namespace Microsoft.VisualStudio.Threading public override void Post(SendOrPostCallback d, object? state) { JoinableTask? job = this.job; // capture as local in case field becomes null later. - if (job != null) + if (job is object) { job.Post(d, state, this.mainThreadAffinitized); } diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs index f6555600..987a5fe3 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -196,44 +193,6 @@ namespace Microsoft.VisualStudio.Threading SynchronouslyBlockingMainThread = 0x20, } - /// - /// Gets JoinableTaskContext for to access locks. - /// - private JoinableTaskContext JoinableTaskContext => this.owner.Context; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private Task QueueNeedProcessEvent - { - get - { - using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) - { - lock (this.JoinableTaskContext.SyncContextLock) - { - if (this.queueNeedProcessEvent == null) - { - // We pass in allowInliningWaiters: true, - // since we control all waiters and their continuations - // are benign, and it makes it more efficient. - this.queueNeedProcessEvent = new AsyncManualResetEvent(allowInliningAwaiters: true); - } - - return this.queueNeedProcessEvent.WaitAsync(); - } - } - } - } - - /// - /// Gets or sets the set of nesting factories (excluding ) - /// that own JoinableTasks that are nesting this one. - /// - internal ListOfOftenOne NestingFactories - { - get { return this.nestingFactories; } - set { this.nestingFactories = value; } - } - /// /// Gets a value indicating whether the async operation represented by this instance has completed. /// @@ -250,12 +209,12 @@ namespace Microsoft.VisualStudio.Threading return false; } - if (this.mainThreadQueue != null && !this.mainThreadQueue.IsCompleted) + if (this.mainThreadQueue is object && !this.mainThreadQueue.IsCompleted) { return false; } - if (this.threadPoolQueue != null && !this.threadPoolQueue.IsCompleted) + if (this.threadPoolQueue is object && !this.threadPoolQueue.IsCompleted) { return false; } @@ -273,13 +232,13 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.wrappedTask == null) + if (this.wrappedTask is null) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { lock (this.JoinableTaskContext.SyncContextLock) { - if (this.wrappedTask == null) + if (this.wrappedTask is null) { // We'd rather not do this. The field is assigned elsewhere later on if we haven't hit this first. // But some caller needs a Task that we don't yet have, so we have to spin one up. @@ -308,6 +267,30 @@ namespace Microsoft.VisualStudio.Threading get { return CompletingTask.Value; } } + /// + /// Gets a value indicating whether an awaiter should capture the + /// . + /// + /// + /// As a library, we generally wouldn't capture the + /// when awaiting, except that where our thread is synchronously blocking anyway, it is actually + /// more efficient to capture the so that the continuation + /// will resume on the blocking thread instead of occupying yet another one in order to execute. + /// In fact, when threadpool starvation conditions exist, resuming on the calling thread + /// can avoid significant delays in executing an often trivial continuation. + /// + internal static bool AwaitShouldCaptureSyncContext => SynchronizationContext.Current is JoinableTaskSynchronizationContext; + + /// + /// Gets or sets the set of nesting factories (excluding ) + /// that own JoinableTasks that are nesting this one. + /// + internal ListOfOftenOne NestingFactories + { + get { return this.nestingFactories; } + set { this.nestingFactories = value; } + } + internal JoinableTaskFactory Factory { get { return this.owner; } @@ -320,13 +303,13 @@ namespace Microsoft.VisualStudio.Threading { if (this.JoinableTaskContext.IsOnMainThread) { - if (this.mainThreadJobSyncContext == null) + if (this.mainThreadJobSyncContext is null) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { lock (this.JoinableTaskContext.SyncContextLock) { - if (this.mainThreadJobSyncContext == null) + if (this.mainThreadJobSyncContext is null) { this.mainThreadJobSyncContext = new JoinableTaskSynchronizationContext(this, true); } @@ -345,13 +328,13 @@ namespace Microsoft.VisualStudio.Threading // must be operable after that point anyway. if (this.SynchronouslyBlockingThreadPool) { - if (this.threadPoolJobSyncContext == null) + if (this.threadPoolJobSyncContext is null) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) { lock (this.JoinableTaskContext.SyncContextLock) { - if (this.threadPoolJobSyncContext == null) + if (this.threadPoolJobSyncContext is null) { this.threadPoolJobSyncContext = new JoinableTaskSynchronizationContext(this, false); } @@ -377,7 +360,7 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.weakSelf == null) + if (this.weakSelf is null) { this.weakSelf = new WeakReference(this); } @@ -402,8 +385,6 @@ namespace Microsoft.VisualStudio.Threading get { return this.creationOptions; } } - #region Diagnostics collection - /// /// Gets the entry method's info so we could show its full name in hang report. /// @@ -419,8 +400,8 @@ namespace Microsoft.VisualStudio.Threading get { Assumes.True(Monitor.IsEntered(this.JoinableTaskContext.SyncContextLock)); - return (this.mainThreadQueue != null && this.mainThreadQueue.Count > 0) - || (this.threadPoolQueue != null && this.threadPoolQueue.Count > 0); + return (this.mainThreadQueue is object && this.mainThreadQueue.Count > 0) + || (this.threadPoolQueue is object && this.threadPoolQueue.Count > 0); } } @@ -434,7 +415,7 @@ namespace Microsoft.VisualStudio.Threading get { Assumes.True(Monitor.IsEntered(this.JoinableTaskContext.SyncContextLock)); - if (this.mainThreadQueue == null) + if (this.mainThreadQueue is null) { return Enumerable.Empty(); } @@ -453,7 +434,7 @@ namespace Microsoft.VisualStudio.Threading get { Assumes.True(Monitor.IsEntered(this.JoinableTaskContext.SyncContextLock)); - if (this.threadPoolQueue == null) + if (this.threadPoolQueue is null) { return Enumerable.Empty(); } @@ -476,8 +457,6 @@ namespace Microsoft.VisualStudio.Threading } } - #endregion - /// /// Gets or sets a value indicating whether this task has had its Complete() method called.. /// @@ -521,18 +500,32 @@ namespace Microsoft.VisualStudio.Threading } /// - /// Gets a value indicating whether an awaiter should capture the - /// . + /// Gets JoinableTaskContext for to access locks. /// - /// - /// As a library, we generally wouldn't capture the - /// when awaiting, except that where our thread is synchronously blocking anyway, it is actually - /// more efficient to capture the so that the continuation - /// will resume on the blocking thread instead of occupying yet another one in order to execute. - /// In fact, when threadpool starvation conditions exist, resuming on the calling thread - /// can avoid significant delays in executing an often trivial continuation. - /// - internal static bool AwaitShouldCaptureSyncContext => SynchronizationContext.Current is JoinableTaskSynchronizationContext; + private JoinableTaskContext JoinableTaskContext => this.owner.Context; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private Task QueueNeedProcessEvent + { + get + { + using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) + { + lock (this.JoinableTaskContext.SyncContextLock) + { + if (this.queueNeedProcessEvent is null) + { + // We pass in allowInliningWaiters: true, + // since we control all waiters and their continuations + // are benign, and it makes it more efficient. + this.queueNeedProcessEvent = new AsyncManualResetEvent(allowInliningAwaiters: true); + } + + return this.queueNeedProcessEvent.WaitAsync(); + } + } + } + } /// /// Synchronously blocks the calling thread until the operation has completed. @@ -573,6 +566,41 @@ namespace Microsoft.VisualStudio.Threading } } + /// + /// Gets an awaiter that is equivalent to calling . + /// + /// A task whose result is the result of the asynchronous operation. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + public TaskAwaiter GetAwaiter() + { + return this.JoinAsync().GetAwaiter(); + } + + ref JoinableTaskDependencyGraph.JoinableTaskDependentData IJoinableTaskDependent.GetJoinableTaskDependentData() + { + return ref this.dependentData; + } + + void IJoinableTaskDependent.OnAddedToDependency(IJoinableTaskDependent parentNode) + { + Requires.NotNull(parentNode, nameof(parentNode)); + this.dependencyParents.Add(parentNode); + } + + void IJoinableTaskDependent.OnRemovedFromDependency(IJoinableTaskDependent parentNode) + { + Requires.NotNull(parentNode, nameof(parentNode)); + this.dependencyParents.Remove(parentNode); + } + + void IJoinableTaskDependent.OnDependencyAdded(IJoinableTaskDependent joinChild) + { + } + + void IJoinableTaskDependent.OnDependencyRemoved(IJoinableTaskDependent joinChild) + { + } + internal void Post(SendOrPostCallback d, object? state, bool mainThreadAffinitized) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) @@ -615,7 +643,7 @@ namespace Microsoft.VisualStudio.Threading { if (mainThreadAffinitized) { - if (this.mainThreadQueue == null) + if (this.mainThreadQueue is null) { this.mainThreadQueue = new ExecutionQueue(this); } @@ -630,7 +658,7 @@ namespace Microsoft.VisualStudio.Threading { if (this.SynchronouslyBlockingThreadPool) { - if (this.threadPoolQueue == null) + if (this.threadPoolQueue is null) { this.threadPoolQueue = new ExecutionQueue(this); } @@ -649,19 +677,19 @@ namespace Microsoft.VisualStudio.Threading if (mainThreadQueueUpdated || backgroundThreadQueueUpdated) { - var tasksNeedNotify = JoinableTaskDependencyGraph.GetDependingSynchronousTasks(this, mainThreadQueueUpdated); + IReadOnlyCollection? tasksNeedNotify = JoinableTaskDependencyGraph.GetDependingSynchronousTasks(this, mainThreadQueueUpdated); if (tasksNeedNotify.Count > 0) { eventsNeedNotify = new List(tasksNeedNotify.Count); - foreach (var taskToNotify in tasksNeedNotify) + foreach (JoinableTask? taskToNotify in tasksNeedNotify) { - if (taskToNotify.pendingEventSource == null || taskToNotify == this) + if (taskToNotify.pendingEventSource is null || taskToNotify == this) { taskToNotify.pendingEventSource = this.WeakSelf; } taskToNotify.pendingEventCount++; - if (taskToNotify.queueNeedProcessEvent != null) + if (taskToNotify.queueNeedProcessEvent is object) { eventsNeedNotify.Add(taskToNotify.queueNeedProcessEvent); } @@ -672,9 +700,9 @@ namespace Microsoft.VisualStudio.Threading } // Notify tasks which can process the event queue. - if (eventsNeedNotify != null) + if (eventsNeedNotify is object) { - foreach (var queueEvent in eventsNeedNotify) + foreach (AsyncManualResetEvent? queueEvent in eventsNeedNotify) { queueEvent.PulseAll(); } @@ -691,7 +719,7 @@ namespace Microsoft.VisualStudio.Threading Assumes.NotNull(wrapper); // this should have been initialized in the above logic. this.owner.PostToUnderlyingSynchronizationContextOrThreadPool(wrapper); - foreach (var nestingFactory in this.nestingFactories) + foreach (JoinableTaskFactory? nestingFactory in this.nestingFactories) { if (nestingFactory != this.owner) { @@ -702,16 +730,6 @@ namespace Microsoft.VisualStudio.Threading } } - /// - /// Gets an awaiter that is equivalent to calling . - /// - /// A task whose result is the result of the asynchronous operation. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - public TaskAwaiter GetAwaiter() - { - return this.JoinAsync().GetAwaiter(); - } - /// /// Instantiate a that can track the ultimate result of . /// @@ -744,7 +762,7 @@ namespace Microsoft.VisualStudio.Threading { lock (this.JoinableTaskContext.SyncContextLock) { - if (this.wrappedTask == null) + if (this.wrappedTask is null) { this.wrappedTask = wrappedTask; } @@ -791,12 +809,12 @@ namespace Microsoft.VisualStudio.Threading { this.IsCompleteRequested = true; - if (this.mainThreadQueue != null) + if (this.mainThreadQueue is object) { this.mainThreadQueue.Complete(); } - if (this.threadPoolQueue != null) + if (this.threadPoolQueue is object) { this.threadPoolQueue.Complete(); } @@ -811,7 +829,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (queueNeedProcessEvent != null) + if (queueNeedProcessEvent is object) { // We explicitly do this outside our lock. queueNeedProcessEvent.PulseAll(); @@ -830,7 +848,7 @@ namespace Microsoft.VisualStudio.Threading try { bool onMainThread = false; - var additionalFlags = JoinableTaskFlags.CompletingSynchronously; + JoinableTaskFlags additionalFlags = JoinableTaskFlags.CompletingSynchronously; if (this.JoinableTaskContext.IsOnMainThread) { additionalFlags |= JoinableTaskFlags.SynchronouslyBlockingMainThread; @@ -874,7 +892,7 @@ namespace Microsoft.VisualStudio.Threading { work.TryExecute(); } - else if (tryAgainAfter != null) + else if (tryAgainAfter is object) { ThreadingEventSource.Instance.WaitSynchronouslyStart(); this.owner.WaitSynchronously(tryAgainAfter); @@ -929,17 +947,17 @@ namespace Microsoft.VisualStudio.Threading // notifications come in. this.JoinableTaskContext.OnJoinableTaskCompleted(this); - foreach (var collection in this.dependencyParents) + foreach (IJoinableTaskDependent? collection in this.dependencyParents) { JoinableTaskDependencyGraph.RemoveDependency(collection, this); } - if (this.mainThreadJobSyncContext != null) + if (this.mainThreadJobSyncContext is object) { this.mainThreadJobSyncContext.OnCompleted(); } - if (this.threadPoolJobSyncContext != null) + if (this.threadPoolJobSyncContext is object) { this.threadPoolJobSyncContext.OnCompleted(); } @@ -950,31 +968,6 @@ namespace Microsoft.VisualStudio.Threading } } - ref JoinableTaskDependencyGraph.JoinableTaskDependentData IJoinableTaskDependent.GetJoinableTaskDependentData() - { - return ref this.dependentData; - } - - void IJoinableTaskDependent.OnAddedToDependency(IJoinableTaskDependent parentNode) - { - Requires.NotNull(parentNode, nameof(parentNode)); - this.dependencyParents.Add(parentNode); - } - - void IJoinableTaskDependent.OnRemovedFromDependency(IJoinableTaskDependent parentNode) - { - Requires.NotNull(parentNode, nameof(parentNode)); - this.dependencyParents.Remove(parentNode); - } - - void IJoinableTaskDependent.OnDependencyAdded(IJoinableTaskDependent joinChild) - { - } - - void IJoinableTaskDependent.OnDependencyRemoved(IJoinableTaskDependent joinChild) - { - } - /// /// Get the number of pending messages to be process for the synchronous task. /// @@ -983,10 +976,10 @@ namespace Microsoft.VisualStudio.Threading internal int GetPendingEventCountForSynchronousTask(JoinableTask synchronousTask) { Requires.NotNull(synchronousTask, nameof(synchronousTask)); - var queue = ((synchronousTask.state & JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTaskFlags.SynchronouslyBlockingMainThread) + ExecutionQueue? queue = ((synchronousTask.state & JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTaskFlags.SynchronouslyBlockingMainThread) ? this.mainThreadQueue : this.threadPoolQueue; - return queue != null ? queue.Count : 0; + return queue is object ? queue.Count : 0; } /// @@ -1000,7 +993,7 @@ namespace Microsoft.VisualStudio.Threading Assumes.True(Monitor.IsEntered(this.JoinableTaskContext.SyncContextLock)); Assumes.True((this.state & JoinableTaskFlags.CompletingSynchronously) == JoinableTaskFlags.CompletingSynchronously); - if (this.pendingEventSource == null || taskHasPendingMessages == this) + if (this.pendingEventSource is null || taskHasPendingMessages == this) { this.pendingEventSource = new WeakReference(taskHasPendingMessages); } @@ -1009,6 +1002,44 @@ namespace Microsoft.VisualStudio.Threading return this.queueNeedProcessEvent; } + private static bool TryDequeueSelfOrDependencies(IJoinableTaskDependent currentNode, bool onMainThread, HashSet visited, [NotNullWhen(true)] out SingleExecuteProtector? work) + { + Requires.NotNull(currentNode, nameof(currentNode)); + Requires.NotNull(visited, nameof(visited)); + Report.IfNot(Monitor.IsEntered(currentNode.JoinableTaskContext.SyncContextLock)); + + // We only need to find the first work item. + work = null; + if (visited.Add(currentNode)) + { + JoinableTask? joinableTask = currentNode as JoinableTask; + if (joinableTask is object) + { + ExecutionQueue? queue = onMainThread ? joinableTask.mainThreadQueue : joinableTask.threadPoolQueue; + if (queue is object && !queue.IsCompleted) + { + queue.TryDequeue(out work); + } + } + + if (work is null) + { + if (joinableTask?.IsCompleted != true) + { + foreach (IJoinableTaskDependent? item in JoinableTaskDependencyGraph.GetDirectDependentNodes(currentNode)) + { + if (TryDequeueSelfOrDependencies(item, onMainThread, visited, out work)) + { + break; + } + } + } + } + } + + return work is object; + } + private bool TryDequeueSelfOrDependencies(bool onMainThread, ref HashSet? visited, [NotNullWhen(true)] out SingleExecuteProtector? work, out Task? tryAgainAfter) { using (this.JoinableTaskContext.NoMessagePumpSynchronizationContext.Apply()) @@ -1026,12 +1057,12 @@ namespace Microsoft.VisualStudio.Threading { this.pendingEventCount--; - if (this.pendingEventSource != null) + if (this.pendingEventSource is object) { if (this.pendingEventSource.TryGetTarget(out JoinableTask? pendingSource) && JoinableTaskDependencyGraph.IsDependingSynchronousTask(pendingSource, this)) { - var queue = onMainThread ? pendingSource.mainThreadQueue : pendingSource.threadPoolQueue; - if (queue != null && !queue.IsCompleted && queue.TryDequeue(out work)) + ExecutionQueue? queue = onMainThread ? pendingSource.mainThreadQueue : pendingSource.threadPoolQueue; + if (queue is object && !queue.IsCompleted && queue.TryDequeue(out work)) { if (queue.Count == 0) { @@ -1046,7 +1077,7 @@ namespace Microsoft.VisualStudio.Threading this.pendingEventSource = null; } - if (visited == null) + if (visited is null) { visited = new HashSet(); } @@ -1071,44 +1102,6 @@ namespace Microsoft.VisualStudio.Threading } } - private static bool TryDequeueSelfOrDependencies(IJoinableTaskDependent currentNode, bool onMainThread, HashSet visited, [NotNullWhen(true)] out SingleExecuteProtector? work) - { - Requires.NotNull(currentNode, nameof(currentNode)); - Requires.NotNull(visited, nameof(visited)); - Report.IfNot(Monitor.IsEntered(currentNode.JoinableTaskContext.SyncContextLock)); - - // We only need to find the first work item. - work = null; - if (visited.Add(currentNode)) - { - JoinableTask? joinableTask = currentNode as JoinableTask; - if (joinableTask != null) - { - var queue = onMainThread ? joinableTask.mainThreadQueue : joinableTask.threadPoolQueue; - if (queue != null && !queue.IsCompleted) - { - queue.TryDequeue(out work); - } - } - - if (work == null) - { - if (joinableTask?.IsCompleted != true) - { - foreach (var item in JoinableTaskDependencyGraph.GetDirectDependentNodes(currentNode)) - { - if (TryDequeueSelfOrDependencies(item, onMainThread, visited, out work)) - { - break; - } - } - } - } - } - - return work != null; - } - /// /// Adds the specified flags to the field. /// @@ -1131,8 +1124,8 @@ namespace Microsoft.VisualStudio.Threading { if (!this.IsCompleted) { - var ambientJob = this.JoinableTaskContext.AmbientTask; - if (ambientJob != null && ambientJob != this) + JoinableTask? ambientJob = this.JoinableTaskContext.AmbientTask; + if (ambientJob is object && ambientJob != this) { return JoinableTaskDependencyGraph.AddDependency(ambientJob, this); } diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskCollection.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskCollection.cs index 6dcdbf71..064b8474 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskCollection.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskCollection.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -135,8 +132,8 @@ namespace Microsoft.VisualStudio.Threading /// public JoinRelease Join() { - var ambientJob = this.Context.AmbientTask; - if (ambientJob == null) + JoinableTask? ambientJob = this.Context.AmbientTask; + if (ambientJob is null) { // The caller isn't running in the context of a joinable task, so there is nothing to join with this collection. return default(JoinRelease); @@ -160,7 +157,7 @@ namespace Microsoft.VisualStudio.Threading { cancellationToken.ThrowIfCancellationRequested(); - if (this.emptyEvent == null) + if (this.emptyEvent is null) { // We need a read lock to protect against the emptiness of this collection changing // while we're setting the initial set state of the new event. @@ -170,14 +167,14 @@ namespace Microsoft.VisualStudio.Threading { // We use interlocked here to mitigate race conditions in lazily initializing this field. // We *could* take a write lock above, but that would needlessly increase lock contention. - var nowait = Interlocked.CompareExchange(ref this.emptyEvent, new AsyncManualResetEvent(JoinableTaskDependencyGraph.HasNoChildDependentNode(this)), null); + AsyncManualResetEvent? nowait = Interlocked.CompareExchange(ref this.emptyEvent, new AsyncManualResetEvent(JoinableTaskDependencyGraph.HasNoChildDependentNode(this)), null); } } } using (this.Join()) { - await this.emptyEvent.WaitAsync().WithCancellation(cancellationToken).ConfigureAwait(false); + await this.emptyEvent.WaitAsync(cancellationToken).ConfigureAwait(false); } } @@ -207,7 +204,7 @@ namespace Microsoft.VisualStudio.Threading var joinables = new List(); lock (this.Context.SyncContextLock) { - foreach (var item in JoinableTaskDependencyGraph.GetDirectDependentNodes(this)) + foreach (IJoinableTaskDependent? item in JoinableTaskDependencyGraph.GetDirectDependentNodes(this)) { if (item is JoinableTask joinableTask) { @@ -238,7 +235,7 @@ namespace Microsoft.VisualStudio.Threading void IJoinableTaskDependent.OnDependencyAdded(IJoinableTaskDependent joinChild) { - if (this.emptyEvent != null && joinChild is JoinableTask) + if (this.emptyEvent is object && joinChild is JoinableTask) { this.emptyEvent.Reset(); } @@ -246,7 +243,7 @@ namespace Microsoft.VisualStudio.Threading void IJoinableTaskDependent.OnDependencyRemoved(IJoinableTaskDependent joinChild) { - if (this.emptyEvent != null && JoinableTaskDependencyGraph.HasNoChildDependentNode(this)) + if (this.emptyEvent is object && JoinableTaskDependencyGraph.HasNoChildDependentNode(this)) { this.emptyEvent.Set(); } @@ -256,7 +253,9 @@ namespace Microsoft.VisualStudio.Threading /// A value whose disposal cancels a operation. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible public struct JoinRelease : IDisposable +#pragma warning restore CA1034 // Nested types should not be visible { private IJoinableTaskDependent? parentDependencyNode; private IJoinableTaskDependent? childDependencyNode; @@ -280,7 +279,7 @@ namespace Microsoft.VisualStudio.Threading /// public void Dispose() { - if (this.parentDependencyNode != null) + if (this.parentDependencyNode is object) { RoslynDebug.Assert(this.childDependencyNode is object, $"{nameof(this.childDependencyNode)} can only be null when {nameof(this.parentDependencyNode)} is null."); diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext+HangReportContributor.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext+HangReportContributor.cs index d5089c6e..9143642e 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext+HangReportContributor.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext+HangReportContributor.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -14,7 +11,7 @@ namespace Microsoft.VisualStudio.Threading using System.Threading.Tasks; using System.Xml.Linq; - partial class JoinableTaskContext : IHangReportContributor + public partial class JoinableTaskContext : IHangReportContributor { /// /// Contributes data for a hang report. @@ -36,11 +33,11 @@ namespace Microsoft.VisualStudio.Threading { lock (this.SyncContextLock) { - var dgml = CreateTemplateDgml(out XElement nodes, out XElement links); + XDocument? dgml = CreateTemplateDgml(out XElement nodes, out XElement links); - var pendingTasksElements = this.CreateNodesForPendingTasks(); - var taskLabels = CreateNodeLabels(pendingTasksElements); - var pendingTaskCollections = CreateNodesForJoinableTaskCollections(pendingTasksElements.Keys); + Dictionary? pendingTasksElements = this.CreateNodesForPendingTasks(); + List>? taskLabels = CreateNodeLabels(pendingTasksElements); + Dictionary? pendingTaskCollections = CreateNodesForJoinableTaskCollections(pendingTasksElements.Keys); nodes.Add(pendingTasksElements.Values); nodes.Add(pendingTaskCollections.Values); nodes.Add(taskLabels.Select(t => t.Item1)); @@ -69,9 +66,9 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(pendingTasksElements, nameof(pendingTasksElements)); var links = new List(); - foreach (var joinableTaskAndElement in pendingTasksElements) + foreach (KeyValuePair joinableTaskAndElement in pendingTasksElements) { - foreach (var joinedTask in JoinableTaskDependencyGraph.GetAllDirectlyDependentJoinableTasks(joinableTaskAndElement.Key)) + foreach (JoinableTask? joinedTask in JoinableTaskDependencyGraph.GetAllDirectlyDependentJoinableTasks(joinableTaskAndElement.Key)) { if (pendingTasksElements.TryGetValue(joinedTask, out XElement? joinedTaskElement)) { @@ -89,11 +86,11 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(collections, nameof(collections)); var result = new List(); - foreach (var task in tasks) + foreach (KeyValuePair task in tasks) { - foreach (var collection in task.Key.ContainingCollections) + foreach (JoinableTaskCollection? collection in task.Key.ContainingCollections) { - var collectionElement = collections[collection]; + XElement? collectionElement = collections[collection]; result.Add(Dgml.Link(collectionElement, task.Value).WithCategories("Contains")); } } @@ -108,11 +105,11 @@ namespace Microsoft.VisualStudio.Threading var collectionsSet = new HashSet(tasks.SelectMany(t => t.ContainingCollections)); var result = new Dictionary(collectionsSet.Count); int collectionId = 0; - foreach (var collection in collectionsSet) + foreach (JoinableTaskCollection? collection in collectionsSet) { collectionId++; var label = string.IsNullOrEmpty(collection.DisplayName) ? "Collection #" + collectionId : collection.DisplayName; - var element = Dgml.Node("Collection#" + collectionId, label, group: "Expanded") + XElement? element = Dgml.Node("Collection#" + collectionId, label, group: "Expanded") .WithCategories("Collection"); result.Add(collection, element); } @@ -125,24 +122,24 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(tasksAndElements, nameof(tasksAndElements)); var result = new List>(); - foreach (var tasksAndElement in tasksAndElements) + foreach (KeyValuePair tasksAndElement in tasksAndElements) { - var pendingTask = tasksAndElement.Key; - var node = tasksAndElement.Value; + JoinableTask? pendingTask = tasksAndElement.Key; + XElement? node = tasksAndElement.Value; int queueIndex = 0; - foreach (var pendingTasksElement in pendingTask.MainThreadQueueContents) + foreach (JoinableTaskFactory.SingleExecuteProtector? pendingTasksElement in pendingTask.MainThreadQueueContents) { queueIndex++; - var callstackNode = Dgml.Node(node.Attribute("Id").Value + "MTQueue#" + queueIndex, GetAsyncReturnStack(pendingTasksElement)); - var callstackLink = Dgml.Link(callstackNode, node); + XElement? callstackNode = Dgml.Node(node.Attribute("Id").Value + "MTQueue#" + queueIndex, GetAsyncReturnStack(pendingTasksElement)); + XElement? callstackLink = Dgml.Link(callstackNode, node); result.Add(Tuple.Create(callstackNode, callstackLink)); } - foreach (var pendingTasksElement in pendingTask.ThreadPoolQueueContents) + foreach (JoinableTaskFactory.SingleExecuteProtector? pendingTasksElement in pendingTask.ThreadPoolQueueContents) { queueIndex++; - var callstackNode = Dgml.Node(node.Attribute("Id").Value + "TPQueue#" + queueIndex, GetAsyncReturnStack(pendingTasksElement)); - var callstackLink = Dgml.Link(callstackNode, node); + XElement? callstackNode = Dgml.Node(node.Attribute("Id").Value + "TPQueue#" + queueIndex, GetAsyncReturnStack(pendingTasksElement)); + XElement? callstackLink = Dgml.Link(callstackNode, node); result.Add(Tuple.Create(callstackNode, callstackLink)); } } @@ -150,46 +147,6 @@ namespace Microsoft.VisualStudio.Threading return result; } - private Dictionary CreateNodesForPendingTasks() - { - var pendingTasksElements = new Dictionary(); - lock (this.pendingTasks) - { - int taskId = 0; - foreach (var pendingTask in this.pendingTasks) - { - taskId++; - - string methodName = string.Empty; - var entryMethodInfo = pendingTask.EntryMethodInfo; - if (entryMethodInfo != null) - { - methodName = string.Format( - CultureInfo.InvariantCulture, - " ({0}.{1})", - entryMethodInfo.DeclaringType?.FullName, - entryMethodInfo.Name); - } - - var node = Dgml.Node("Task#" + taskId, "Task #" + taskId + methodName) - .WithCategories("Task"); - if (pendingTask.HasNonEmptyQueue) - { - node.WithCategories("NonEmptyQueue"); - } - - if (pendingTask.State.HasFlag(JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread)) - { - node.WithCategories("MainThreadBlocking"); - } - - pendingTasksElements.Add(pendingTask, node); - } - } - - return pendingTasksElements; - } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private static string GetAsyncReturnStack(JoinableTaskFactory.SingleExecuteProtector singleExecuteProtector) { @@ -211,5 +168,45 @@ namespace Microsoft.VisualStudio.Threading return stringBuilder.ToString().TrimEnd(); } + + private Dictionary CreateNodesForPendingTasks() + { + var pendingTasksElements = new Dictionary(); + lock (this.pendingTasks) + { + int taskId = 0; + foreach (JoinableTask? pendingTask in this.pendingTasks) + { + taskId++; + + string methodName = string.Empty; + System.Reflection.MethodInfo? entryMethodInfo = pendingTask.EntryMethodInfo; + if (entryMethodInfo is object) + { + methodName = string.Format( + CultureInfo.InvariantCulture, + " ({0}.{1})", + entryMethodInfo.DeclaringType?.FullName, + entryMethodInfo.Name); + } + + XElement? node = Dgml.Node("Task#" + taskId, "Task #" + taskId + methodName) + .WithCategories("Task"); + if (pendingTask.HasNonEmptyQueue) + { + node.WithCategories("NonEmptyQueue"); + } + + if (pendingTask.State.HasFlag(JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread)) + { + node.WithCategories("MainThreadBlocking"); + } + + pendingTasksElements.Add(pendingTask, node); + } + } + + return pendingTasksElements; + } } } diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs index fcf353e0..5c635ffd 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskContext.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -161,13 +158,13 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.nonJoinableFactory == null) + if (this.nonJoinableFactory is null) { using (this.NoMessagePumpSynchronizationContext.Apply()) { lock (this.SyncContextLock) { - if (this.nonJoinableFactory == null) + if (this.nonJoinableFactory is null) { this.nonJoinableFactory = this.CreateDefaultFactory(); } @@ -200,7 +197,7 @@ namespace Microsoft.VisualStudio.Threading /// public bool IsWithinJoinableTask { - get { return this.AmbientTask != null; } + get { return this.AmbientTask is object; } } /// @@ -294,8 +291,8 @@ namespace Microsoft.VisualStudio.Threading /// public bool IsMainThreadBlocked() { - var ambientTask = this.AmbientTask; - if (ambientTask != null) + JoinableTask? ambientTask = this.AmbientTask; + if (ambientTask is object) { if (JoinableTaskDependencyGraph.HasMainThreadSynchronousTaskWaiting(ambientTask)) { @@ -317,7 +314,7 @@ namespace Microsoft.VisualStudio.Threading { // our read lock doesn't cover this collection var allJoinedJobs = new HashSet(); - foreach (var initializingTask in this.initializingSynchronouslyMainThreadTasks) + foreach (JoinableTask? initializingTask in this.initializingSynchronouslyMainThreadTasks) { if (!JoinableTaskDependencyGraph.HasMainThreadSynchronousTaskWaiting(initializingTask)) { @@ -369,98 +366,6 @@ namespace Microsoft.VisualStudio.Threading GC.SuppressFinalize(this); } - /// - /// Disposes managed and unmanaged resources held by this instance. - /// - /// true if was called; false if the object is being finalized. - protected virtual void Dispose(bool disposing) - { - } - - /// - /// Invoked when a hang is suspected to have occurred involving the main thread. - /// - /// The duration of the current hang. - /// The number of times this hang has been reported, including this one. - /// A random GUID that uniquely identifies this particular hang. - /// - /// A single hang occurrence may invoke this method multiple times, with increasing - /// values in the parameter. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] - protected internal virtual void OnHangDetected(TimeSpan hangDuration, int notificationCount, Guid hangId) - { - List listeners; - using (this.NoMessagePumpSynchronizationContext.Apply()) - { - lock (this.hangNotifications) - { - listeners = this.hangNotifications.ToList(); - } - } - - var blockingTask = JoinableTask.TaskCompletingOnThisThread; - var hangDetails = new HangDetails( - hangDuration, - notificationCount, - hangId, - blockingTask?.EntryMethodInfo); - foreach (var listener in listeners) - { - try - { - listener.OnHangDetected(hangDetails); - } - catch (Exception ex) - { - // Report it in CHK, but don't throw. In a hang situation, we don't want the product - // to fail for another reason, thus hiding the hang issue. - Report.Fail("Exception thrown from OnHangDetected listener. {0}", ex); - } - } - } - - /// - /// Invoked when an earlier hang report is false alarm. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] - protected internal virtual void OnFalseHangDetected(TimeSpan hangDuration, Guid hangId) - { - List listeners; - using (this.NoMessagePumpSynchronizationContext.Apply()) - { - lock (this.hangNotifications) - { - listeners = this.hangNotifications.ToList(); - } - } - - foreach (var listener in listeners) - { - try - { - listener.OnFalseHangDetected(hangDuration, hangId); - } - catch (Exception ex) - { - // Report it in CHK, but don't throw. In a hang situation, we don't want the product - // to fail for another reason, thus hiding the hang issue. - Report.Fail("Exception thrown from OnHangDetected listener. {0}", ex); - } - } - } - - /// - /// Creates a factory without a . - /// - /// - /// Used for initializing the property. - /// - protected internal virtual JoinableTaskFactory CreateDefaultFactory() - { - return new JoinableTaskFactory(this); - } - /// /// Raised when a joinable task starts. /// @@ -545,6 +450,98 @@ namespace Microsoft.VisualStudio.Threading return new HangNotificationRegistration(node); } + /// + /// Invoked when a hang is suspected to have occurred involving the main thread. + /// + /// The duration of the current hang. + /// The number of times this hang has been reported, including this one. + /// A random GUID that uniquely identifies this particular hang. + /// + /// A single hang occurrence may invoke this method multiple times, with increasing + /// values in the parameter. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected internal virtual void OnHangDetected(TimeSpan hangDuration, int notificationCount, Guid hangId) + { + List listeners; + using (this.NoMessagePumpSynchronizationContext.Apply()) + { + lock (this.hangNotifications) + { + listeners = this.hangNotifications.ToList(); + } + } + + JoinableTask? blockingTask = JoinableTask.TaskCompletingOnThisThread; + var hangDetails = new HangDetails( + hangDuration, + notificationCount, + hangId, + blockingTask?.EntryMethodInfo); + foreach (JoinableTaskContextNode? listener in listeners) + { + try + { + listener.OnHangDetected(hangDetails); + } + catch (Exception ex) + { + // Report it in CHK, but don't throw. In a hang situation, we don't want the product + // to fail for another reason, thus hiding the hang issue. + Report.Fail("Exception thrown from OnHangDetected listener. {0}", ex); + } + } + } + + /// + /// Invoked when an earlier hang report is false alarm. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + protected internal virtual void OnFalseHangDetected(TimeSpan hangDuration, Guid hangId) + { + List listeners; + using (this.NoMessagePumpSynchronizationContext.Apply()) + { + lock (this.hangNotifications) + { + listeners = this.hangNotifications.ToList(); + } + } + + foreach (JoinableTaskContextNode? listener in listeners) + { + try + { + listener.OnFalseHangDetected(hangDuration, hangId); + } + catch (Exception ex) + { + // Report it in CHK, but don't throw. In a hang situation, we don't want the product + // to fail for another reason, thus hiding the hang issue. + Report.Fail("Exception thrown from OnHangDetected listener. {0}", ex); + } + } + } + + /// + /// Creates a factory without a . + /// + /// + /// Used for initializing the property. + /// + protected internal virtual JoinableTaskFactory CreateDefaultFactory() + { + return new JoinableTaskFactory(this); + } + + /// + /// Disposes managed and unmanaged resources held by this instance. + /// + /// true if was called; false if the object is being finalized. + protected virtual void Dispose(bool disposing) + { + } + /// /// A structure that clears CallContext and SynchronizationContext async/thread statics and /// restores those values when this structure is disposed. @@ -588,7 +585,7 @@ namespace Microsoft.VisualStudio.Threading /// public void Dispose() { - if (this.pump != null) + if (this.pump is object) { this.pump.AmbientTask = this.oldJoinable; } @@ -597,52 +594,14 @@ namespace Microsoft.VisualStudio.Threading } } - /// - /// A value whose disposal cancels hang registration. - /// - private class HangNotificationRegistration : IDisposable - { - /// - /// The node to receive notifications. May be null if has already been called. - /// - private JoinableTaskContextNode? node; - - /// - /// Initializes a new instance of the class. - /// - internal HangNotificationRegistration(JoinableTaskContextNode node) - { - Requires.NotNull(node, nameof(node)); - this.node = node; - } - - /// - /// Removes the node from hang notifications. - /// - public void Dispose() - { - var node = this.node; - if (node != null) - { - using (node.Context.NoMessagePumpSynchronizationContext.Apply()) - { - lock (node.Context.hangNotifications) - { - Assumes.True(node.Context.hangNotifications.Remove(node)); - } - } - - this.node = null; - } - } - } - /// /// A class to encapsulate the details of a possible hang. /// An instance of this class will be passed to the /// instances who registered the hang notifications. /// +#pragma warning disable CA1034 // Nested types should not be visible public class HangDetails +#pragma warning restore CA1034 // Nested types should not be visible { /// Initializes a new instance of the class. /// The duration of the current hang. @@ -687,5 +646,45 @@ namespace Microsoft.VisualStudio.Threading /// public MethodInfo? EntryMethod { get; private set; } } + + /// + /// A value whose disposal cancels hang registration. + /// + private class HangNotificationRegistration : IDisposable + { + /// + /// The node to receive notifications. May be null if has already been called. + /// + private JoinableTaskContextNode? node; + + /// + /// Initializes a new instance of the class. + /// + internal HangNotificationRegistration(JoinableTaskContextNode node) + { + Requires.NotNull(node, nameof(node)); + this.node = node; + } + + /// + /// Removes the node from hang notifications. + /// + public void Dispose() + { + JoinableTaskContextNode? node = this.node; + if (node is object) + { + using (node.Context.NoMessagePumpSynchronizationContext.Apply()) + { + lock (node.Context.hangNotifications) + { + Assumes.True(node.Context.hangNotifications.Remove(node)); + } + } + + this.node = null; + } + } + } } } diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskContextException.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskContextException.cs index 8dc42fa0..fa0b5801 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskContextException.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskContextException.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskContextNode.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskContextNode.cs index 021663fd..aefd29c6 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskContextNode.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskContextNode.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -49,9 +46,9 @@ namespace Microsoft.VisualStudio.Threading { get { - if (this.nonJoinableFactory == null) + if (this.nonJoinableFactory is null) { - var factory = this.CreateDefaultFactory(); + JoinableTaskFactory? factory = this.CreateDefaultFactory(); Interlocked.CompareExchange(ref this.nonJoinableFactory, factory, null); } @@ -141,20 +138,6 @@ namespace Microsoft.VisualStudio.Threading return this.context.IsMainThreadBlocked(); } - /// - /// Invoked when a hang is suspected to have occurred involving the main thread. - /// - /// The duration of the current hang. - /// The number of times this hang has been reported, including this one. - /// A random GUID that uniquely identifies this particular hang. - /// - /// A single hang occurrence may invoke this method multiple times, with increasing - /// values in the parameter. - /// - protected virtual void OnHangDetected(TimeSpan hangDuration, int notificationCount, Guid hangId) - { - } - /// /// Invoked when a hang is suspected to have occurred involving the main thread. /// @@ -181,6 +164,20 @@ namespace Microsoft.VisualStudio.Threading { } + /// + /// Invoked when a hang is suspected to have occurred involving the main thread. + /// + /// The duration of the current hang. + /// The number of times this hang has been reported, including this one. + /// A random GUID that uniquely identifies this particular hang. + /// + /// A single hang occurrence may invoke this method multiple times, with increasing + /// values in the parameter. + /// + protected virtual void OnHangDetected(TimeSpan hangDuration, int notificationCount, Guid hangId) + { + } + /// /// Creates a factory without a . /// diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskCreationOptions.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskCreationOptions.cs index 04cecc59..39fb0d4f 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskCreationOptions.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskCreationOptions.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskDependencyGraph.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskDependencyGraph.cs index 0faccfe6..7d82f614 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskDependencyGraph.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskDependencyGraph.cs @@ -1,11 +1,9 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { + using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -175,16 +173,6 @@ namespace Microsoft.VisualStudio.Threading /// internal struct JoinableTaskDependentData { - /// - /// Shared empty collection to prevent extra allocations. - /// - private static readonly JoinableTask[] EmptyJoinableTaskArray = new JoinableTask[0]; - - /// - /// Shared empty collection to prevent extra allocations. - /// - private static readonly PendingNotification[] EmptyPendingNotificationArray = new PendingNotification[0]; - /// /// A map of jobs that we should be willing to dequeue from when we control the UI thread, and a ref count. Lazily constructed. /// @@ -203,55 +191,7 @@ namespace Microsoft.VisualStudio.Threading /// /// Gets a value indicating whether the is empty. /// - internal bool HasNoChildDependentNode => this.childDependentNodes == null || this.childDependentNodes.Count == 0 || !this.childDependentNodes.Any(); - - /// - /// Gets all dependent nodes registered in the - /// This method is expected to be used with the JTF lock. - /// - internal IEnumerable GetDirectDependentNodes() - { - if (this.childDependentNodes == null) - { - return Enumerable.Empty(); - } - - return this.childDependentNodes.Keys; - } - - /// - /// Checks whether a dependent node is inside . - /// This method is expected to be used with the JTF lock. - /// - internal bool HasDirectDependency(IJoinableTaskDependent dependency) - { - if (this.childDependentNodes == null) - { - return false; - } - - return this.childDependentNodes.ContainsKey(dependency); - } - - /// - /// Gets a value indicating whether the main thread is waiting for the task's completion - /// This method is expected to be used with the JTF lock. - /// - internal bool HasMainThreadSynchronousTaskWaiting() - { - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) - { - if ((existingTaskTracking.SynchronousTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) - { - return true; - } - - existingTaskTracking = existingTaskTracking.Next; - } - - return false; - } + internal bool HasNoChildDependentNode => this.childDependentNodes is null || this.childDependentNodes.Count == 0 || !this.childDependentNodes.Any(); /// /// Gets a snapshot of all joined tasks. @@ -263,7 +203,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(taskOrCollection, nameof(taskOrCollection)); Assumes.True(Monitor.IsEntered(taskOrCollection.JoinableTaskContext.SyncContextLock)); - if (taskOrCollection.GetJoinableTaskDependentData().childDependentNodes == null) + if (taskOrCollection.GetJoinableTaskDependentData().childDependentNodes is null) { return Enumerable.Empty(); } @@ -273,30 +213,6 @@ namespace Microsoft.VisualStudio.Threading return allTasks; } - /// - /// Remove all synchronous tasks tracked by the this task. - /// This is called when this task is completed. - /// This method is expected to be used with the JTF lock. - /// - internal void OnTaskCompleted() - { - if (this.dependingSynchronousTaskTracking != null) - { - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - this.dependingSynchronousTaskTracking = null; - - if (this.childDependentNodes != null) - { - var childrenTasks = new List(this.childDependentNodes.Keys); - while (existingTaskTracking != null) - { - RemoveDependingSynchronousTaskFrom(childrenTasks, existingTaskTracking.SynchronousTask, false); - existingTaskTracking = existingTaskTracking.Next; - } - } - } - } - /// /// Adds a instance as one that is relevant to the async operation. /// @@ -324,7 +240,7 @@ namespace Microsoft.VisualStudio.Threading } ref JoinableTaskDependentData data = ref parentTaskOrCollection.GetJoinableTaskDependentData(); - if (data.childDependentNodes == null) + if (data.childDependentNodes is null) { data.childDependentNodes = new WeakKeyDictionary(capacity: 2); } @@ -339,14 +255,14 @@ namespace Microsoft.VisualStudio.Threading { // This constitutes a significant change, so we should apply synchronous task tracking to the new child. joinChild.OnAddedToDependency(parentTaskOrCollection); - var tasksNeedNotify = AddDependingSynchronousTaskToChild(parentTaskOrCollection, joinChild); + IReadOnlyCollection? tasksNeedNotify = AddDependingSynchronousTaskToChild(parentTaskOrCollection, joinChild); if (tasksNeedNotify.Count > 0) { eventsNeedNotify = new List(tasksNeedNotify.Count); - foreach (var taskToNotify in tasksNeedNotify) + foreach (PendingNotification taskToNotify in tasksNeedNotify) { - var notifyEvent = taskToNotify.SynchronousTask.RegisterPendingEventsForSynchrousTask(taskToNotify.TaskHasPendingMessages, taskToNotify.NewPendingMessagesCount); - if (notifyEvent != null) + AsyncManualResetEvent? notifyEvent = taskToNotify.SynchronousTask.RegisterPendingEventsForSynchrousTask(taskToNotify.TaskHasPendingMessages, taskToNotify.NewPendingMessagesCount); + if (notifyEvent is object) { eventsNeedNotify.Add(notifyEvent); } @@ -358,9 +274,9 @@ namespace Microsoft.VisualStudio.Threading } // We explicitly do this outside our lock. - if (eventsNeedNotify != null) + if (eventsNeedNotify is object) { - foreach (var queueEvent in eventsNeedNotify) + foreach (AsyncManualResetEvent? queueEvent in eventsNeedNotify) { queueEvent.PulseAll(); } @@ -385,7 +301,7 @@ namespace Microsoft.VisualStudio.Threading ref JoinableTaskDependentData data = ref parentTaskOrCollection.GetJoinableTaskDependentData(); lock (parentTaskOrCollection.JoinableTaskContext.SyncContextLock) { - if (data.childDependentNodes != null && data.childDependentNodes.TryGetValue(joinChild, out int refCount)) + if (data.childDependentNodes is object && data.childDependentNodes.TryGetValue(joinChild, out int refCount)) { if (refCount == 1) { @@ -422,67 +338,16 @@ namespace Microsoft.VisualStudio.Threading } } - var childDependentNodes = taskOrCollection.GetJoinableTaskDependentData().childDependentNodes; - if (childDependentNodes != null) + WeakKeyDictionary? childDependentNodes = taskOrCollection.GetJoinableTaskDependentData().childDependentNodes; + if (childDependentNodes is object) { - foreach (var item in childDependentNodes) + foreach (KeyValuePair item in childDependentNodes) { AddSelfAndDescendentOrJoinedJobs(item.Key, joinables); } } } - /// - /// Check whether a task is being tracked in our tracking list. - /// - internal bool IsDependingSynchronousTask(JoinableTask syncTask) - { - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) - { - if (existingTaskTracking.SynchronousTask == syncTask) - { - return true; - } - - existingTaskTracking = existingTaskTracking.Next; - } - - return false; - } - - /// - /// Calculate the collection of events we need trigger after we enqueue a request. - /// This method is expected to be used with the JTF lock. - /// - /// True if we want to find tasks to process the main thread queue. Otherwise tasks to process the background queue. - /// The collection of synchronous tasks we need notify. - internal IReadOnlyCollection GetDependingSynchronousTasks(bool forMainThread) - { - int count = this.CountOfDependingSynchronousTasks(); - if (count == 0) - { - return EmptyJoinableTaskArray; - } - - var tasksNeedNotify = new List(count); - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) - { - var syncTask = existingTaskTracking.SynchronousTask; - bool syncTaskInOnMainThread = (syncTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread; - if (forMainThread == syncTaskInOnMainThread) - { - // Only synchronous tasks are in the list, so we don't need do further check for the CompletingSynchronously flag - tasksNeedNotify.Add(syncTask); - } - - existingTaskTracking = existingTaskTracking.Next; - } - - return tasksNeedNotify; - } - /// /// When the current dependent node is a synchronous task, this method is called before the thread is blocked to wait it to complete. /// This adds the current task to the of the task itself (which will propergate through its dependencies.) @@ -522,19 +387,126 @@ namespace Microsoft.VisualStudio.Threading } /// - /// Get how many number of synchronous tasks in our tracking list. + /// Gets all dependent nodes registered in the + /// This method is expected to be used with the JTF lock. /// - private int CountOfDependingSynchronousTasks() + internal IEnumerable GetDirectDependentNodes() { - int count = 0; - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) + if (this.childDependentNodes is null) { - count++; + return Enumerable.Empty(); + } + + return this.childDependentNodes.Keys; + } + + /// + /// Checks whether a dependent node is inside . + /// This method is expected to be used with the JTF lock. + /// + internal bool HasDirectDependency(IJoinableTaskDependent dependency) + { + if (this.childDependentNodes is null) + { + return false; + } + + return this.childDependentNodes.ContainsKey(dependency); + } + + /// + /// Gets a value indicating whether the main thread is waiting for the task's completion + /// This method is expected to be used with the JTF lock. + /// + internal bool HasMainThreadSynchronousTaskWaiting() + { + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + while (existingTaskTracking is object) + { + if ((existingTaskTracking.SynchronousTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) + { + return true; + } + existingTaskTracking = existingTaskTracking.Next; } - return count; + return false; + } + + /// + /// Remove all synchronous tasks tracked by the this task. + /// This is called when this task is completed. + /// This method is expected to be used with the JTF lock. + /// + internal void OnTaskCompleted() + { + if (this.dependingSynchronousTaskTracking is object) + { + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + this.dependingSynchronousTaskTracking = null; + + if (this.childDependentNodes is object) + { + var childrenTasks = new List(this.childDependentNodes.Keys); + while (existingTaskTracking is object) + { + RemoveDependingSynchronousTaskFrom(childrenTasks, existingTaskTracking.SynchronousTask, false); + existingTaskTracking = existingTaskTracking.Next; + } + } + } + } + + /// + /// Check whether a task is being tracked in our tracking list. + /// + internal bool IsDependingSynchronousTask(JoinableTask syncTask) + { + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + while (existingTaskTracking is object) + { + if (existingTaskTracking.SynchronousTask == syncTask) + { + return true; + } + + existingTaskTracking = existingTaskTracking.Next; + } + + return false; + } + + /// + /// Calculate the collection of events we need trigger after we enqueue a request. + /// This method is expected to be used with the JTF lock. + /// + /// True if we want to find tasks to process the main thread queue. Otherwise tasks to process the background queue. + /// The collection of synchronous tasks we need notify. + internal IReadOnlyCollection GetDependingSynchronousTasks(bool forMainThread) + { + int count = this.CountOfDependingSynchronousTasks(); + if (count == 0) + { + return Array.Empty(); + } + + var tasksNeedNotify = new List(count); + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + while (existingTaskTracking is object) + { + JoinableTask? syncTask = existingTaskTracking.SynchronousTask; + bool syncTaskInOnMainThread = (syncTask.State & JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread) == JoinableTask.JoinableTaskFlags.SynchronouslyBlockingMainThread; + if (forMainThread == syncTaskInOnMainThread) + { + // Only synchronous tasks are in the list, so we don't need do further check for the CompletingSynchronously flag + tasksNeedNotify.Add(syncTask); + } + + existingTaskTracking = existingTaskTracking.Next; + } + + return tasksNeedNotify; } /// @@ -553,16 +525,16 @@ namespace Microsoft.VisualStudio.Threading int count = data.CountOfDependingSynchronousTasks(); if (count == 0) { - return EmptyPendingNotificationArray; + return Array.Empty(); } var tasksNeedNotify = new List(count); DependentSynchronousTask? existingTaskTracking = data.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) + while (existingTaskTracking is object) { int totalEventNumber = 0; - var eventTriggeringTask = AddDependingSynchronousTask(child, existingTaskTracking.SynchronousTask, ref totalEventNumber); - if (eventTriggeringTask != null) + JoinableTask? eventTriggeringTask = AddDependingSynchronousTask(child, existingTaskTracking.SynchronousTask, ref totalEventNumber); + if (eventTriggeringTask is object) { tasksNeedNotify.Add(new PendingNotification(existingTaskTracking.SynchronousTask, eventTriggeringTask, totalEventNumber)); } @@ -573,22 +545,6 @@ namespace Microsoft.VisualStudio.Threading return tasksNeedNotify; } - /// - /// Removes all synchronous tasks we applies to a dependent task, after the relationship is removed. - /// - /// The original dependent task. - private void RemoveDependingSynchronousTaskFromChild(IJoinableTaskDependent child) - { - Requires.NotNull(child, nameof(child)); - - DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) - { - RemoveDependingSynchronousTask(child, existingTaskTracking.SynchronousTask); - existingTaskTracking = existingTaskTracking.Next; - } - } - /// /// Tracks a new synchronous task for this task. /// A synchronous task is a task blocking a thread and waits it to be completed. We may want the blocking thread @@ -605,7 +561,7 @@ namespace Microsoft.VisualStudio.Threading Assumes.True(Monitor.IsEntered(taskOrCollection.JoinableTaskContext.SyncContextLock)); JoinableTask? thisJoinableTask = taskOrCollection as JoinableTask; - if (thisJoinableTask != null) + if (thisJoinableTask is object) { if (thisJoinableTask.IsCompleted) { @@ -628,7 +584,7 @@ namespace Microsoft.VisualStudio.Threading ref JoinableTaskDependentData data = ref taskOrCollection.GetJoinableTaskDependentData(); DependentSynchronousTask? existingTaskTracking = data.dependingSynchronousTaskTracking; - while (existingTaskTracking != null) + while (existingTaskTracking is object) { if (existingTaskTracking.SynchronousTask == synchronousTask) { @@ -641,7 +597,7 @@ namespace Microsoft.VisualStudio.Threading JoinableTask? eventTriggeringTask = null; - if (thisJoinableTask != null) + if (thisJoinableTask is object) { int pendingItemCount = thisJoinableTask.GetPendingEventCountForSynchronousTask(synchronousTask); if (pendingItemCount > 0) @@ -658,12 +614,12 @@ namespace Microsoft.VisualStudio.Threading }; data.dependingSynchronousTaskTracking = newTaskTracking; - if (data.childDependentNodes != null) + if (data.childDependentNodes is object) { - foreach (var item in data.childDependentNodes) + foreach (KeyValuePair item in data.childDependentNodes) { - var childTiggeringTask = AddDependingSynchronousTask(item.Key, synchronousTask, ref totalEventsPending); - if (eventTriggeringTask == null) + JoinableTask? childTiggeringTask = AddDependingSynchronousTask(item.Key, synchronousTask, ref totalEventsPending); + if (eventTriggeringTask is null) { eventTriggeringTask = childTiggeringTask; } @@ -686,7 +642,7 @@ namespace Microsoft.VisualStudio.Threading Assumes.True(Monitor.IsEntered(taskOrCollection.JoinableTaskContext.SyncContextLock)); var syncTaskItem = (IJoinableTaskDependent)syncTask; - if (syncTaskItem.GetJoinableTaskDependentData().dependingSynchronousTaskTracking != null) + if (syncTaskItem.GetJoinableTaskDependentData().dependingSynchronousTaskTracking is object) { RemoveDependingSynchronousTaskFrom(new IJoinableTaskDependent[] { taskOrCollection }, syncTask, force); } @@ -711,12 +667,12 @@ namespace Microsoft.VisualStudio.Threading reachableNodes = new HashSet(); } - foreach (var task in tasks) + foreach (IJoinableTaskDependent? task in tasks) { RemoveDependingSynchronousTask(task, syncTask, reachableNodes, ref remainNodes); } - if (!force && remainNodes != null && remainNodes.Count > 0) + if (!force && remainNodes is object && remainNodes.Count > 0) { // a set of tasks may form a dependent loop, so it will make the reference count system // not to work correctly when we try to remove the synchronous task. @@ -729,7 +685,7 @@ namespace Microsoft.VisualStudio.Threading // force to remove all invalid items HashSet? remainPlaceHold = null; - foreach (var remainTask in remainNodes) + foreach (IJoinableTaskDependent? remainTask in remainNodes) { RemoveDependingSynchronousTask(remainTask, syncTask, reachableNodes, ref remainPlaceHold); } @@ -758,10 +714,10 @@ namespace Microsoft.VisualStudio.Threading return; } - var dependencies = taskOrCollection.GetJoinableTaskDependentData().childDependentNodes; - if (dependencies != null) + WeakKeyDictionary? dependencies = taskOrCollection.GetJoinableTaskDependentData().childDependentNodes; + if (dependencies is object) { - foreach (var item in dependencies) + foreach (KeyValuePair item in dependencies) { ComputeSelfAndDescendentOrJoinedJobsAndRemainTasks(item.Key, reachableNodes, remainNodes); } @@ -789,13 +745,13 @@ namespace Microsoft.VisualStudio.Threading DependentSynchronousTask? currentTaskTracking = data.dependingSynchronousTaskTracking; bool removed = false; - while (currentTaskTracking != null) + while (currentTaskTracking is object) { if (currentTaskTracking.SynchronousTask == task) { if (--currentTaskTracking.ReferenceCount > 0) { - if (reachableNodes != null) + if (reachableNodes is object) { if (!reachableNodes.Contains(taskOrCollection)) { @@ -807,7 +763,7 @@ namespace Microsoft.VisualStudio.Threading if (currentTaskTracking.ReferenceCount == 0) { removed = true; - if (previousTaskTracking != null) + if (previousTaskTracking is object) { previousTaskTracking.Next = currentTaskTracking.Next; } @@ -817,18 +773,18 @@ namespace Microsoft.VisualStudio.Threading } } - if (reachableNodes == null) + if (reachableNodes is null) { if (removed) { - if (remainingDependentNodes != null) + if (remainingDependentNodes is object) { remainingDependentNodes.Remove(taskOrCollection); } } else { - if (remainingDependentNodes == null) + if (remainingDependentNodes is null) { remainingDependentNodes = new HashSet(); } @@ -844,15 +800,47 @@ namespace Microsoft.VisualStudio.Threading currentTaskTracking = currentTaskTracking.Next; } - if (removed && data.childDependentNodes != null) + if (removed && data.childDependentNodes is object) { - foreach (var item in data.childDependentNodes) + foreach (KeyValuePair item in data.childDependentNodes) { RemoveDependingSynchronousTask(item.Key, task, reachableNodes, ref remainingDependentNodes); } } } + /// + /// Get how many number of synchronous tasks in our tracking list. + /// + private int CountOfDependingSynchronousTasks() + { + int count = 0; + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + while (existingTaskTracking is object) + { + count++; + existingTaskTracking = existingTaskTracking.Next; + } + + return count; + } + + /// + /// Removes all synchronous tasks we applies to a dependent task, after the relationship is removed. + /// + /// The original dependent task. + private void RemoveDependingSynchronousTaskFromChild(IJoinableTaskDependent child) + { + Requires.NotNull(child, nameof(child)); + + DependentSynchronousTask? existingTaskTracking = this.dependingSynchronousTaskTracking; + while (existingTaskTracking is object) + { + RemoveDependingSynchronousTask(child, existingTaskTracking.SynchronousTask); + existingTaskTracking = existingTaskTracking.Next; + } + } + /// /// The record of a pending notification we need send to the synchronous task that we have some new messages to process. /// diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs index 7b74dcb8..434c6fdc 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -75,7 +72,7 @@ namespace Microsoft.VisualStudio.Threading internal JoinableTaskFactory(JoinableTaskContext owner, JoinableTaskCollection? collection) { Requires.NotNull(owner, nameof(owner)); - Assumes.True(collection == null || collection.Context == owner); + Assumes.True(collection is null || collection.Context == owner); this.owner = owner; this.jobCollection = collection; @@ -205,6 +202,169 @@ namespace Microsoft.VisualStudio.Threading return new MainThreadAwaitable(this, this.Context.AmbientTask, cancellationToken, alwaysYield); } + /// + /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. + /// + /// The asynchronous method to execute. + /// + /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + /// + /// // On threadpool or Main thread, this method will block + /// // the calling thread until all async operations in the + /// // delegate complete. + /// joinableTaskFactory.Run(async delegate { + /// // still on the threadpool or Main thread as before. + /// await OperationAsync(); + /// // still on the threadpool or Main thread as before. + /// await Task.Run(async delegate { + /// // Now we're on a threadpool thread. + /// await Task.Yield(); + /// // still on a threadpool thread. + /// }); + /// // Now back on the Main thread (or threadpool thread if that's where we started). + /// }); + /// + /// + /// + public void Run(Func asyncMethod) + { + this.Run(asyncMethod, JoinableTaskCreationOptions.None, entrypointOverride: null); + } + + /// + /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. + /// + /// The asynchronous method to execute. + /// The used to customize the task's behavior. + public void Run(Func asyncMethod, JoinableTaskCreationOptions creationOptions) + { + this.Run(asyncMethod, creationOptions, entrypointOverride: null); + } + + /// + /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. + /// + /// The type of value returned by the asynchronous operation. + /// The asynchronous method to execute. + /// The result of the Task returned by . + /// + /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// See the overload documentation for an example. + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public T Run(Func> asyncMethod) + { + return this.Run(asyncMethod, JoinableTaskCreationOptions.None); + } + + /// + /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. + /// + /// The type of value returned by the asynchronous operation. + /// The asynchronous method to execute. + /// The used to customize the task's behavior. + /// The result of the Task returned by . + /// + /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] + public T Run(Func> asyncMethod, JoinableTaskCreationOptions creationOptions) + { + VerifyNoNonConcurrentSyncContext(); + JoinableTask? joinable = this.RunAsync(asyncMethod, synchronouslyBlocking: true, creationOptions: creationOptions); + return joinable.CompleteOnCurrentThread(); + } + + /// + /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. + /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method + /// requires the main thread while the main thread is blocked waiting for the async method's completion. + /// + /// The method that, when executed, will begin the async operation. + /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. + /// + /// Exceptions thrown by the delegate are captured by the returned . + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + public JoinableTask RunAsync(Func asyncMethod) + { + return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: JoinableTaskCreationOptions.None); + } + + /// + /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. + /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method + /// requires the main thread while the main thread is blocked waiting for the async method's completion. + /// + /// The method that, when executed, will begin the async operation. + /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. + /// The used to customize the task's behavior. + /// + /// Exceptions thrown by the delegate are captured by the returned . + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + public JoinableTask RunAsync(Func asyncMethod, JoinableTaskCreationOptions creationOptions) + { + return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: creationOptions); + } + + /// + /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. + /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method + /// requires the main thread while the main thread is blocked waiting for the async method's completion. + /// + /// The type of value returned by the asynchronous operation. + /// The method that, when executed, will begin the async operation. + /// + /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. + /// + /// + /// Exceptions thrown by the delegate are captured by the returned . + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + public JoinableTask RunAsync(Func> asyncMethod) + { + return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: JoinableTaskCreationOptions.None); + } + + /// + /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. + /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method + /// requires the main thread while the main thread is blocked waiting for the async method's completion. + /// + /// The type of value returned by the asynchronous operation. + /// The method that, when executed, will begin the async operation. + /// The used to customize the task's behavior. + /// + /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. + /// + /// + /// Exceptions thrown by the delegate are captured by the returned . + /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context + /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution + /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. + /// + public JoinableTask RunAsync(Func> asyncMethod, JoinableTaskCreationOptions creationOptions) + { + return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: creationOptions); + } + /// /// Responds to calls to /// by scheduling a continuation to execute on the Main thread. @@ -220,11 +380,11 @@ namespace Microsoft.VisualStudio.Threading // there is no ambient job, or the ambient job does not belong to the collection, we must create // a (child) job and add that to this job factory's collection so that folks joining that factory // can help this switch to complete. - var ambientJob = this.Context.AmbientTask; + JoinableTask? ambientJob = this.Context.AmbientTask; SingleExecuteProtector? wrapper = null; - if (ambientJob == null || (this.jobCollection != null && !this.jobCollection.Contains(ambientJob))) + if (ambientJob is null || (this.jobCollection is object && !this.jobCollection.Contains(ambientJob))) { - var transient = this.RunAsync( + JoinableTask? transient = this.RunAsync( delegate { RoslynDebug.Assert(this.Context.AmbientTask is object, $"{nameof(this.Context.AmbientTask)} is always set for {nameof(this.RunAsync)} callbacks."); @@ -254,6 +414,61 @@ namespace Microsoft.VisualStudio.Threading return wrapper; } + /// + /// Posts a callback to the main thread via the underlying dispatcher, + /// or to the threadpool when no dispatcher exists on the main thread. + /// + internal void PostToUnderlyingSynchronizationContextOrThreadPool(SingleExecuteProtector callback) + { + Requires.NotNull(callback, nameof(callback)); + + if (this.UnderlyingSynchronizationContext is object) + { + this.PostToUnderlyingSynchronizationContext(SingleExecuteProtector.ExecuteOnce, callback); + } + else + { + ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, callback); + } + } + + /// Runs the specified asynchronous method. + /// The asynchronous method to execute. + /// The used to customize the task's behavior. + /// The delegate to record as the entrypoint for this JoinableTask. + internal void Run(Func asyncMethod, JoinableTaskCreationOptions creationOptions, Delegate? entrypointOverride) + { + VerifyNoNonConcurrentSyncContext(); + JoinableTask? joinable = this.RunAsync(asyncMethod, synchronouslyBlocking: true, creationOptions: creationOptions, entrypointOverride: entrypointOverride); + joinable.CompleteOnCurrentThread(); + } + + internal void Post(SendOrPostCallback callback, object? state, bool mainThreadAffinitized) + { + Requires.NotNull(callback, nameof(callback)); + + if (mainThreadAffinitized) + { + JoinableTask? transient = this.RunAsync(delegate + { + RoslynDebug.Assert(this.Context.AmbientTask is object, $"{nameof(this.Context.AmbientTask)} is always set for {nameof(this.RunAsync)} callbacks."); + + this.Context.AmbientTask.Post(callback, state, true); + return Task.CompletedTask; + }); + + if (transient.Task.IsFaulted) + { + // rethrow the exception. + transient.Task.GetAwaiter().GetResult(); + } + } + else + { + ThreadPool.QueueUserWorkItem(new WaitCallback(callback), state); + } + } + /// /// Posts a message to the specified underlying SynchronizationContext for processing when the main thread /// is freely available. @@ -293,24 +508,6 @@ namespace Microsoft.VisualStudio.Threading Requires.NotNull(joinableTask, nameof(joinableTask)); } - /// - /// Posts a callback to the main thread via the underlying dispatcher, - /// or to the threadpool when no dispatcher exists on the main thread. - /// - internal void PostToUnderlyingSynchronizationContextOrThreadPool(SingleExecuteProtector callback) - { - Requires.NotNull(callback, nameof(callback)); - - if (this.UnderlyingSynchronizationContext != null) - { - this.PostToUnderlyingSynchronizationContext(SingleExecuteProtector.ExecuteOnce, callback); - } - else - { - ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, callback); - } - } - /// /// Synchronously blocks the calling thread for the completion of the specified task. /// If running on the main thread, any applicable message pump is suppressed @@ -400,8 +597,8 @@ namespace Microsoft.VisualStudio.Threading /// Return true if the current synchronous task on the thread is waiting on a long running task. protected bool IsWaitingOnLongRunningTask() { - var currentBlockingTask = JoinableTask.TaskCompletingOnThisThread; - if (currentBlockingTask != null) + JoinableTask? currentBlockingTask = JoinableTask.TaskCompletingOnThisThread; + if (currentBlockingTask is object) { if ((currentBlockingTask.CreationOptions & JoinableTaskCreationOptions.LongRunning) == JoinableTaskCreationOptions.LongRunning) { @@ -422,231 +619,13 @@ namespace Microsoft.VisualStudio.Threading return false; } - /// - /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. - /// - /// The asynchronous method to execute. - /// - /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - /// - /// // On threadpool or Main thread, this method will block - /// // the calling thread until all async operations in the - /// // delegate complete. - /// joinableTaskFactory.Run(async delegate { - /// // still on the threadpool or Main thread as before. - /// await OperationAsync(); - /// // still on the threadpool or Main thread as before. - /// await Task.Run(async delegate { - /// // Now we're on a threadpool thread. - /// await Task.Yield(); - /// // still on a threadpool thread. - /// }); - /// // Now back on the Main thread (or threadpool thread if that's where we started). - /// }); - /// - /// - /// - public void Run(Func asyncMethod) - { - this.Run(asyncMethod, JoinableTaskCreationOptions.None, entrypointOverride: null); - } - - /// - /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. - /// - /// The asynchronous method to execute. - /// The used to customize the task's behavior. - public void Run(Func asyncMethod, JoinableTaskCreationOptions creationOptions) - { - this.Run(asyncMethod, creationOptions, entrypointOverride: null); - } - - /// - /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. - /// - /// The type of value returned by the asynchronous operation. - /// The asynchronous method to execute. - /// The result of the Task returned by . - /// - /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// See the overload documentation for an example. - /// - [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] - public T Run(Func> asyncMethod) - { - return this.Run(asyncMethod, JoinableTaskCreationOptions.None); - } - - /// - /// Runs the specified asynchronous method to completion while synchronously blocking the calling thread. - /// - /// The type of value returned by the asynchronous operation. - /// The asynchronous method to execute. - /// The used to customize the task's behavior. - /// The result of the Task returned by . - /// - /// Any exception thrown by the delegate is rethrown in its original type to the caller of this method. - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] - public T Run(Func> asyncMethod, JoinableTaskCreationOptions creationOptions) - { - VerifyNoNonConcurrentSyncContext(); - var joinable = this.RunAsync(asyncMethod, synchronouslyBlocking: true, creationOptions: creationOptions); - return joinable.CompleteOnCurrentThread(); - } - - /// - /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. - /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method - /// requires the main thread while the main thread is blocked waiting for the async method's completion. - /// - /// The method that, when executed, will begin the async operation. - /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. - /// - /// Exceptions thrown by the delegate are captured by the returned . - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - public JoinableTask RunAsync(Func asyncMethod) - { - return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: JoinableTaskCreationOptions.None); - } - - /// - /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. - /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method - /// requires the main thread while the main thread is blocked waiting for the async method's completion. - /// - /// The method that, when executed, will begin the async operation. - /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. - /// The used to customize the task's behavior. - /// - /// Exceptions thrown by the delegate are captured by the returned . - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - public JoinableTask RunAsync(Func asyncMethod, JoinableTaskCreationOptions creationOptions) - { - return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: creationOptions); - } - - /// Runs the specified asynchronous method. - /// The asynchronous method to execute. - /// The used to customize the task's behavior. - /// The delegate to record as the entrypoint for this JoinableTask. - internal void Run(Func asyncMethod, JoinableTaskCreationOptions creationOptions, Delegate? entrypointOverride) - { - VerifyNoNonConcurrentSyncContext(); - var joinable = this.RunAsync(asyncMethod, synchronouslyBlocking: true, creationOptions: creationOptions, entrypointOverride: entrypointOverride); - joinable.CompleteOnCurrentThread(); - } - - /// - /// Wraps the invocation of an async method such that it may - /// execute asynchronously, but may potentially be - /// synchronously completed (waited on) in the future. - /// - /// The asynchronous method to execute. - /// A value indicating whether the launching thread will synchronously block for this job's completion. - /// The used to customize the task's behavior. - /// The entry method's info for diagnostics. - private JoinableTask RunAsync(Func asyncMethod, bool synchronouslyBlocking, JoinableTaskCreationOptions creationOptions, Delegate? entrypointOverride = null) - { - Requires.NotNull(asyncMethod, nameof(asyncMethod)); - - var job = new JoinableTask(this, synchronouslyBlocking, creationOptions, entrypointOverride ?? asyncMethod); - this.ExecuteJob(asyncMethod, job); - return job; - } - - /// - /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. - /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method - /// requires the main thread while the main thread is blocked waiting for the async method's completion. - /// - /// The type of value returned by the asynchronous operation. - /// The method that, when executed, will begin the async operation. - /// - /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. - /// - /// - /// Exceptions thrown by the delegate are captured by the returned . - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - public JoinableTask RunAsync(Func> asyncMethod) - { - return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: JoinableTaskCreationOptions.None); - } - - /// - /// Invokes an async delegate on the caller's thread, and yields back to the caller when the async method yields. - /// The async delegate is invoked in such a way as to mitigate deadlocks in the event that the async method - /// requires the main thread while the main thread is blocked waiting for the async method's completion. - /// - /// The type of value returned by the asynchronous operation. - /// The method that, when executed, will begin the async operation. - /// The used to customize the task's behavior. - /// - /// An object that tracks the completion of the async operation, and allows for later synchronous blocking of the main thread for completion if necessary. - /// - /// - /// Exceptions thrown by the delegate are captured by the returned . - /// When the delegate resumes from a yielding await, the default behavior is to resume in its original context - /// as an ordinary async method execution would. For example, if the caller was on the main thread, execution - /// resumes after an await on the main thread; but if it started on a threadpool thread it resumes on a threadpool thread. - /// - public JoinableTask RunAsync(Func> asyncMethod, JoinableTaskCreationOptions creationOptions) - { - return this.RunAsync(asyncMethod, synchronouslyBlocking: false, creationOptions: creationOptions); - } - - internal void Post(SendOrPostCallback callback, object? state, bool mainThreadAffinitized) - { - Requires.NotNull(callback, nameof(callback)); - - if (mainThreadAffinitized) - { - var transient = this.RunAsync(delegate - { - RoslynDebug.Assert(this.Context.AmbientTask is object, $"{nameof(this.Context.AmbientTask)} is always set for {nameof(this.RunAsync)} callbacks."); - - this.Context.AmbientTask.Post(callback, state, true); - return Task.CompletedTask; - }); - - if (transient.Task.IsFaulted) - { - // rethrow the exception. - transient.Task.GetAwaiter().GetResult(); - } - } - else - { - ThreadPool.QueueUserWorkItem(new WaitCallback(callback), state); - } - } - /// /// Adds the specified joinable task to the applicable collection. /// protected void Add(JoinableTask joinable) { Requires.NotNull(joinable, nameof(joinable)); - if (this.jobCollection != null) + if (this.jobCollection is object) { this.jobCollection.Add(joinable); } @@ -679,6 +658,24 @@ namespace Microsoft.VisualStudio.Threading } } + /// + /// Wraps the invocation of an async method such that it may + /// execute asynchronously, but may potentially be + /// synchronously completed (waited on) in the future. + /// + /// The asynchronous method to execute. + /// A value indicating whether the launching thread will synchronously block for this job's completion. + /// The used to customize the task's behavior. + /// The entry method's info for diagnostics. + private JoinableTask RunAsync(Func asyncMethod, bool synchronouslyBlocking, JoinableTaskCreationOptions creationOptions, Delegate? entrypointOverride = null) + { + Requires.NotNull(asyncMethod, nameof(asyncMethod)); + + var job = new JoinableTask(this, synchronouslyBlocking, creationOptions, entrypointOverride ?? asyncMethod); + this.ExecuteJob(asyncMethod, job); + return job; + } + private JoinableTask RunAsync(Func> asyncMethod, bool synchronouslyBlocking, JoinableTaskCreationOptions creationOptions) { Requires.NotNull(asyncMethod, nameof(asyncMethod)); @@ -755,7 +752,9 @@ namespace Microsoft.VisualStudio.Threading /// An awaiter struct that facilitates an asynchronous transition to the Main thread. /// [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct MainThreadAwaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible { private static readonly Action SafeCancellationAction = state => ThreadPool.QueueUserWorkItem(SingleExecuteProtector.ExecuteOnceWaitCallback, state); @@ -818,9 +817,9 @@ namespace Microsoft.VisualStudio.Threading } return this.synchronousCancellation - || this.jobFactory == null + || this.jobFactory is null || this.jobFactory.Context.IsOnMainThread - || this.jobFactory.Context.UnderlyingSynchronizationContext == null; + || this.jobFactory.Context.UnderlyingSynchronizationContext is null; } } @@ -848,14 +847,14 @@ namespace Microsoft.VisualStudio.Threading /// public void GetResult() { - Assumes.True(this.jobFactory != null); - if (!(this.jobFactory.Context.IsOnMainThread || this.jobFactory.Context.UnderlyingSynchronizationContext == null || this.cancellationToken.IsCancellationRequested)) + Assumes.True(this.jobFactory is object); + if (!(this.jobFactory.Context.IsOnMainThread || this.jobFactory.Context.UnderlyingSynchronizationContext is null || this.cancellationToken.IsCancellationRequested)) { throw new JoinableTaskContextException(Strings.SwitchToMainThreadFailedToReachExpectedThread); } // Release memory associated with the cancellation request. - if (this.cancellationRegistrationPtr != null) + if (this.cancellationRegistrationPtr is object) { CancellationTokenRegistration registration = default(CancellationTokenRegistration); using (this.jobFactory.Context.NoMessagePumpSynchronizationContext.Apply()) @@ -893,7 +892,7 @@ namespace Microsoft.VisualStudio.Threading // We don't have an opportunity to revert the sync context change, but it turns out we don't need to because // this method should only be called from async methods, which automatically revert any execution context // changes they apply (including SynchronizationContext) when they complete, thanks to the way .NET 4.5 works. - var syncContext = this.job != null ? this.job.ApplicableJobSyncContext : this.jobFactory.ApplicableJobSyncContext; + SynchronizationContext? syncContext = this.job is object ? this.job.ApplicableJobSyncContext : this.jobFactory.ApplicableJobSyncContext; syncContext.Apply(); // Cancel if requested, even if we arrived on the main thread. @@ -911,7 +910,7 @@ namespace Microsoft.VisualStudio.Threading [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Environment.FailFast(System.String,System.Exception)"), SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Failing here is worth crashing the process for.")] private void OnCompleted(Action continuation, bool flowExecutionContext) { - Assumes.True(this.jobFactory != null); + Assumes.True(this.jobFactory is object); bool restoreFlow = !flowExecutionContext && !ExecutionContext.IsFlowSuppressed(); if (restoreFlow) @@ -925,15 +924,15 @@ namespace Microsoft.VisualStudio.Threading // or the main thread will execute the continuation first. So we must wrap the continuation // in a SingleExecuteProtector so that it can't be executed twice by accident. // Success case of the main thread. - var wrapper = this.jobFactory.RequestSwitchToMainThread(continuation); + SingleExecuteProtector? wrapper = this.jobFactory.RequestSwitchToMainThread(continuation); // Cancellation case of a threadpool thread. - if (this.cancellationRegistrationPtr != null) + if (this.cancellationRegistrationPtr is object) { // Store the cancellation token registration in the struct pointer. This way, // if the awaiter has been copied (since it's a struct), each copy of the awaiter // points to the same registration. Without this we can have a memory leak. - var registration = this.cancellationToken.Register( + CancellationTokenRegistration registration = this.cancellationToken.Register( NullableHelpers.AsNullableArgAction(flowExecutionContext ? SafeCancellationAction : UnsafeCancellationAction), wrapper, useSynchronizationContext: false); @@ -1012,12 +1011,12 @@ namespace Microsoft.VisualStudio.Threading this.syncContextRevert = this.joinable.ApplicableJobSyncContext.Apply(); // Join the ambient parent job, so the parent can dequeue this job's work. - if (this.previousJoinable != null && !this.previousJoinable.IsCompleted) + if (this.previousJoinable is object && !this.previousJoinable.IsCompleted) { JoinableTaskDependencyGraph.AddDependency(this.previousJoinable, joinable); // By definition we inherit the nesting factories of our immediate nesting task. - var nestingFactories = this.previousJoinable.NestingFactories; + ListOfOftenOne nestingFactories = this.previousJoinable.NestingFactories; // And we may add our immediate nesting parent's factory to the list of // ancestors if it isn't already in the list. @@ -1096,7 +1095,7 @@ namespace Microsoft.VisualStudio.Threading /// internal bool HasBeenExecuted { - get { return this.invokeDelegate == null; } + get { return this.invokeDelegate is null; } } /// @@ -1112,65 +1111,6 @@ namespace Microsoft.VisualStudio.Threading } } - /// - /// Registers for a callback when this instance is executed. - /// - internal void AddExecutingCallback(JoinableTask.ExecutionQueue callbackReceiver) - { - if (!this.HasBeenExecuted) - { - this.executingCallbacks.Add(callbackReceiver); - } - } - - /// - /// Unregisters a callback for when this instance is executed. - /// - internal void RemoveExecutingCallback(JoinableTask.ExecutionQueue callbackReceiver) - { - this.executingCallbacks.Remove(callbackReceiver); - } - - /// - /// Walk the continuation objects inside "async state machines" to generate the return callstack. - /// FOR DIAGNOSTIC PURPOSES ONLY. - /// - internal IEnumerable WalkAsyncReturnStackFrames() - { - // This instance might be a wrapper of another instance of "SingleExecuteProtector". - // If that is true, we need to follow the chain to find the inner instance of "SingleExecuteProtector". - var singleExecuteProtector = this; - while (singleExecuteProtector.state is SingleExecuteProtector) - { - singleExecuteProtector = (SingleExecuteProtector)singleExecuteProtector.state; - } - - var invokeDelegate = singleExecuteProtector.invokeDelegate as Delegate; - var stateDelegate = singleExecuteProtector.state as Delegate; - - // We are in favor of "state" when "invokeDelegate" is a static method and "state" is the actual delegate. - Delegate? actualDelegate = (stateDelegate != null && stateDelegate.Target != null) ? stateDelegate : invokeDelegate; - if (actualDelegate == null) - { - yield return ""; - yield break; - } - - foreach (var frame in actualDelegate.GetAsyncReturnStackFrames()) - { - yield return frame; - } - } - - internal void RaiseTransitioningEvents() - { - Assumes.False(this.raiseTransitionComplete); // if this method is called twice, that's the sign of a problem. - RoslynDebug.Assert(this.job is object); - - this.raiseTransitionComplete = true; - this.job.Factory.OnTransitioningToMainThread(this.job); - } - /// /// Initializes a new instance of the class. /// @@ -1200,7 +1140,7 @@ namespace Microsoft.VisualStudio.Threading // As an optimization, recognize if what we're being handed is already an instance of this type, // because if it is, we don't need to wrap it with yet another instance. var existing = state as SingleExecuteProtector; - if (callback == ExecuteOnce && existing != null && job == existing.job) + if (callback == ExecuteOnce && existing is object && job == existing.job) { return existing; } @@ -1212,17 +1152,76 @@ namespace Microsoft.VisualStudio.Threading }; } + /// + /// Registers for a callback when this instance is executed. + /// + internal void AddExecutingCallback(JoinableTask.ExecutionQueue callbackReceiver) + { + if (!this.HasBeenExecuted) + { + this.executingCallbacks.Add(callbackReceiver); + } + } + + /// + /// Unregisters a callback for when this instance is executed. + /// + internal void RemoveExecutingCallback(JoinableTask.ExecutionQueue callbackReceiver) + { + this.executingCallbacks.Remove(callbackReceiver); + } + + /// + /// Walk the continuation objects inside "async state machines" to generate the return callstack. + /// FOR DIAGNOSTIC PURPOSES ONLY. + /// + internal IEnumerable WalkAsyncReturnStackFrames() + { + // This instance might be a wrapper of another instance of "SingleExecuteProtector". + // If that is true, we need to follow the chain to find the inner instance of "SingleExecuteProtector". + SingleExecuteProtector? singleExecuteProtector = this; + while (singleExecuteProtector.state is SingleExecuteProtector) + { + singleExecuteProtector = (SingleExecuteProtector)singleExecuteProtector.state; + } + + var invokeDelegate = singleExecuteProtector.invokeDelegate as Delegate; + var stateDelegate = singleExecuteProtector.state as Delegate; + + // We are in favor of "state" when "invokeDelegate" is a static method and "state" is the actual delegate. + Delegate? actualDelegate = (stateDelegate is object && stateDelegate.Target is object) ? stateDelegate : invokeDelegate; + if (actualDelegate is null) + { + yield return ""; + yield break; + } + + foreach (var frame in actualDelegate.GetAsyncReturnStackFrames()) + { + yield return frame; + } + } + + internal void RaiseTransitioningEvents() + { + Assumes.False(this.raiseTransitionComplete); // if this method is called twice, that's the sign of a problem. + RoslynDebug.Assert(this.job is object); + + this.raiseTransitionComplete = true; + this.job.Factory.OnTransitioningToMainThread(this.job); + } + /// /// Executes the delegate if it has not already executed. /// internal bool TryExecute() { object? invokeDelegate = Interlocked.Exchange(ref this.invokeDelegate, null); - if (invokeDelegate != null) + if (invokeDelegate is object) { this.OnExecuting(); #pragma warning disable CS8602 // Dereference of a possibly null reference. - var syncContext = this.job != null ? this.job.ApplicableJobSyncContext : this.job.Factory.ApplicableJobSyncContext; + SynchronizationContext? syncContext = this.job is object ? this.job.ApplicableJobSyncContext : this.job.Factory.ApplicableJobSyncContext; #pragma warning restore CS8602 // Dereference of a possibly null reference. using (syncContext.Apply(checkForChangesOnRevert: false)) { @@ -1262,7 +1261,7 @@ namespace Microsoft.VisualStudio.Threading // While raising the event, automatically remove the handlers since we'll only // raise them once, and we'd like to avoid holding references that may extend // the lifetime of our recipients. - using (var enumerator = this.executingCallbacks.EnumerateAndClear()) + using (ListOfOftenOne.Enumerator enumerator = this.executingCallbacks.EnumerateAndClear()) { while (enumerator.MoveNext()) { diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTask`1.cs b/src/Microsoft.VisualStudio.Threading/JoinableTask`1.cs index 46b42aca..681d9d3f 100644 --- a/src/Microsoft.VisualStudio.Threading/JoinableTask`1.cs +++ b/src/Microsoft.VisualStudio.Threading/JoinableTask`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/LightUps.cs b/src/Microsoft.VisualStudio.Threading/LightUps.cs index 08aff879..cd936531 100644 --- a/src/Microsoft.VisualStudio.Threading/LightUps.cs +++ b/src/Microsoft.VisualStudio.Threading/LightUps.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -13,16 +10,16 @@ namespace Microsoft.VisualStudio.Threading /// internal static class LightUps { - /// - /// The for Windows 8. - /// - private static readonly Version Windows8Version = new Version(6, 2, 9200); - /// /// Gets a value indicating whether we execute Windows 7 code even on later versions of Windows. /// internal const bool ForceWindows7Mode = false; + /// + /// The for Windows 8. + /// + private static readonly Version Windows8Version = new Version(6, 2, 9200); + /// /// Gets a value indicating whether the current operating system is Windows 8 or later. /// diff --git a/src/Microsoft.VisualStudio.Threading/ListOfOftenOne.cs b/src/Microsoft.VisualStudio.Threading/ListOfOftenOne`1.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading/ListOfOftenOne.cs rename to src/Microsoft.VisualStudio.Threading/ListOfOftenOne`1.cs index 2c156dc0..ec12f39b 100644 --- a/src/Microsoft.VisualStudio.Threading/ListOfOftenOne.cs +++ b/src/Microsoft.VisualStudio.Threading/ListOfOftenOne`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -66,7 +63,8 @@ namespace Microsoft.VisualStudio.Threading priorValue = Volatile.Read(ref this.value); var newValue = Combine(priorValue, value); fieldBeforeExchange = Interlocked.CompareExchange(ref this.value, newValue, priorValue); - } while (priorValue != fieldBeforeExchange); + } + while (priorValue != fieldBeforeExchange); } /// @@ -81,7 +79,8 @@ namespace Microsoft.VisualStudio.Threading priorValue = Volatile.Read(ref this.value); var newValue = Remove(priorValue, value); fieldBeforeExchange = Interlocked.CompareExchange(ref this.value, newValue, priorValue); - } while (priorValue != fieldBeforeExchange); + } + while (priorValue != fieldBeforeExchange); } /// @@ -95,7 +94,7 @@ namespace Microsoft.VisualStudio.Threading /// public bool Contains(T value) { - foreach (var item in this) + foreach (T? item in this) { if (item == value) { @@ -126,7 +125,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(value, nameof(value)); - if (baseValue == null) + if (baseValue is null) { return value; } @@ -151,7 +150,7 @@ namespace Microsoft.VisualStudio.Threading /// The new value to store as the collection. private static object? Remove(object? baseValue, T value) { - if (baseValue == value || baseValue == null) + if (baseValue == value || baseValue is null) { return null; } @@ -225,7 +224,7 @@ namespace Microsoft.VisualStudio.Threading public bool MoveNext() { - if (this.currentIndex == IndexBeforeSingleElement && this.enumeratedValue != null) + if (this.currentIndex == IndexBeforeSingleElement && this.enumeratedValue is object) { this.currentIndex = IndexSingleElement; return true; diff --git a/src/Microsoft.VisualStudio.Threading/Microsoft.VisualStudio.Threading.csproj b/src/Microsoft.VisualStudio.Threading/Microsoft.VisualStudio.Threading.csproj index 71018583..fa8476cd 100644 --- a/src/Microsoft.VisualStudio.Threading/Microsoft.VisualStudio.Threading.csproj +++ b/src/Microsoft.VisualStudio.Threading/Microsoft.VisualStudio.Threading.csproj @@ -1,8 +1,6 @@  netstandard2.0;netcoreapp3.0;net472 - true - FxCopRules.ruleset Async synchronization primitives, async collections, TPL and dataflow extensions. 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). diff --git a/src/Microsoft.VisualStudio.Threading/NativeMethods.cs b/src/Microsoft.VisualStudio.Threading/NativeMethods.cs index 0b4e0a5d..faeb4895 100644 --- a/src/Microsoft.VisualStudio.Threading/NativeMethods.cs +++ b/src/Microsoft.VisualStudio.Threading/NativeMethods.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -15,6 +12,12 @@ namespace Microsoft.VisualStudio.Threading /// internal static partial class NativeMethods { + /// + /// Indicates that the lifetime of the registration must not be tied to the lifetime of the thread issuing the RegNotifyChangeKeyValue call. + /// Note: This flag value is only supported in Windows 8 and later. + /// + internal const RegistryChangeNotificationFilters REG_NOTIFY_THREAD_AGNOSTIC = (RegistryChangeNotificationFilters)0x10000000L; + /// /// Really truly non pumping wait. /// Raw IntPtrs have to be used, because the marshaller does not support arrays of SafeHandle, only @@ -27,12 +30,6 @@ namespace Microsoft.VisualStudio.Threading [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] internal static extern int WaitForMultipleObjects(uint handleCount, IntPtr[] waitHandles, [MarshalAs(UnmanagedType.Bool)] bool waitAll, uint millisecondsTimeout); - /// - /// Indicates that the lifetime of the registration must not be tied to the lifetime of the thread issuing the RegNotifyChangeKeyValue call. - /// Note: This flag value is only supported in Windows 8 and later. - /// - internal const RegistryChangeNotificationFilters REG_NOTIFY_THREAD_AGNOSTIC = (RegistryChangeNotificationFilters)0x10000000L; - /// /// Registers to receive notification of changes to a registry key. /// diff --git a/src/Microsoft.VisualStudio.Threading/NoMessagePumpSyncContext.cs b/src/Microsoft.VisualStudio.Threading/NoMessagePumpSyncContext.cs index 7dd39b12..8f89a2eb 100644 --- a/src/Microsoft.VisualStudio.Threading/NoMessagePumpSyncContext.cs +++ b/src/Microsoft.VisualStudio.Threading/NoMessagePumpSyncContext.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/NonConcurrentSynchronizationContext.cs b/src/Microsoft.VisualStudio.Threading/NonConcurrentSynchronizationContext.cs index 6957b109..69debc08 100644 --- a/src/Microsoft.VisualStudio.Threading/NonConcurrentSynchronizationContext.cs +++ b/src/Microsoft.VisualStudio.Threading/NonConcurrentSynchronizationContext.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -117,13 +118,15 @@ namespace Microsoft.VisualStudio.Threading this.Post( s2 => { - var (cb, s, m) = (Tuple>)s2!; + (SendOrPostCallback cb, object s, TaskCompletionSource m) = (Tuple>)s2!; try { cb(s); m.SetResult(null); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { m.SetException(ex); } @@ -146,7 +149,7 @@ namespace Microsoft.VisualStudio.Threading { while (true) { - var work = await this.queue!.DequeueAsync().ConfigureAwait(false); + (SendOrPostCallback, object?) work = await this.queue!.DequeueAsync().ConfigureAwait(false); this.activeManagedThreadId = Environment.CurrentManagedThreadId; try { @@ -155,7 +158,9 @@ namespace Microsoft.VisualStudio.Threading work.Item1(work.Item2); } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { this.UnhandledException?.Invoke(this, ex); } @@ -165,7 +170,9 @@ namespace Microsoft.VisualStudio.Threading } } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { // A failure to schedule work is fatal because it can lead to hangs that are // very hard to diagnose to a failure in the scheduler, and even harder to identify diff --git a/src/Microsoft.VisualStudio.Threading/NullableHelpers.cs b/src/Microsoft.VisualStudio.Threading/NullableHelpers.cs index 6d8a667b..c5600777 100644 --- a/src/Microsoft.VisualStudio.Threading/NullableHelpers.cs +++ b/src/Microsoft.VisualStudio.Threading/NullableHelpers.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/ProgressWithCompletion.cs b/src/Microsoft.VisualStudio.Threading/ProgressWithCompletion`1.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading/ProgressWithCompletion.cs rename to src/Microsoft.VisualStudio.Threading/ProgressWithCompletion`1.cs index 747d096b..971ee75c 100644 --- a/src/Microsoft.VisualStudio.Threading/ProgressWithCompletion.cs +++ b/src/Microsoft.VisualStudio.Threading/ProgressWithCompletion`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -111,7 +108,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(handler, nameof(handler)); this.handler = handler; - if (joinableTaskFactory != null) + if (joinableTaskFactory is object) { this.joinableTaskFactory = joinableTaskFactory; this.outstandingJoinableTasks = joinableTaskFactory.Context.CreateCollection(); @@ -120,30 +117,11 @@ namespace Microsoft.VisualStudio.Threading else { this.syncObject = new object(); - this.taskFactory = new TaskFactory(SynchronizationContext.Current != null ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default); + this.taskFactory = new TaskFactory(SynchronizationContext.Current is object ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default); this.outstandingTasks = new HashSet(); } } - private bool IsJoinableTaskAware( - [NotNullWhen(true)] out JoinableTaskFactory? joinableTaskFactory, - [NotNullWhen(true)] out JoinableTaskCollection? outstandingJoinableTasks, - [NotNullWhen(false)] out object? syncObject, - [NotNullWhen(false)] out HashSet? outstandingTasks, - [NotNullWhen(false)] out TaskFactory? taskFactory) - { - joinableTaskFactory = this.joinableTaskFactory; - outstandingJoinableTasks = this.outstandingJoinableTasks; - - syncObject = this.syncObject; - outstandingTasks = this.outstandingTasks; - taskFactory = this.taskFactory; - -#pragma warning disable CS8762 // Parameter may not have a null value when exiting in some condition. - return joinableTaskFactory is object; -#pragma warning restore CS8762 // Parameter may not have a null value when exiting in some condition. - } - /// /// Receives a progress update. /// @@ -153,13 +131,39 @@ namespace Microsoft.VisualStudio.Threading this.Report(value); } + /// + /// Returns a task that completes when all reported progress has executed. + /// + /// A task that completes when all progress is complete. + public Task WaitAsync() => this.WaitAsync(CancellationToken.None); + + /// + /// Returns a task that completes when all reported progress has executed. + /// + /// A cancellation token. + /// A task that completes when all progress is complete. + public Task WaitAsync(CancellationToken cancellationToken) + { + if (this.IsJoinableTaskAware(out _, out JoinableTaskCollection? outstandingJoinableTasks, out var syncObject, out HashSet? outstandingTasks, out _)) + { + return outstandingJoinableTasks.JoinTillEmptyAsync(cancellationToken); + } + else + { + lock (syncObject) + { + return Task.WhenAll(outstandingTasks).WithCancellation(cancellationToken); + } + } + } + /// /// Receives a progress update. /// /// The value representing the updated progress. protected virtual void Report(T value) { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out var outstandingJoinableTasks, out var syncObject, out var outstandingTasks, out var taskFactory)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out JoinableTaskCollection? outstandingJoinableTasks, out var syncObject, out HashSet? outstandingTasks, out TaskFactory? taskFactory)) { JoinableTask joinableTask = joinableTaskFactory.RunAsync( async delegate @@ -183,7 +187,7 @@ namespace Microsoft.VisualStudio.Threading else { #pragma warning disable CA2008 // Do not create tasks without passing a TaskScheduler - var reported = taskFactory.StartNew(() => this.handler(value)).Unwrap(); + Task? reported = taskFactory.StartNew(() => this.handler(value)).Unwrap(); #pragma warning restore CA2008 // Do not create tasks without passing a TaskScheduler lock (syncObject) { @@ -204,32 +208,6 @@ namespace Microsoft.VisualStudio.Threading } } - /// - /// Returns a task that completes when all reported progress has executed. - /// - /// A task that completes when all progress is complete. - public Task WaitAsync() => this.WaitAsync(CancellationToken.None); - - /// - /// Returns a task that completes when all reported progress has executed. - /// - /// A cancellation token. - /// A task that completes when all progress is complete. - public Task WaitAsync(CancellationToken cancellationToken) - { - if (this.IsJoinableTaskAware(out _, out var outstandingJoinableTasks, out var syncObject, out var outstandingTasks, out _)) - { - return outstandingJoinableTasks.JoinTillEmptyAsync(cancellationToken); - } - else - { - lock (syncObject) - { - return Task.WhenAll(outstandingTasks).WithCancellation(cancellationToken); - } - } - } - private static Func WrapSyncHandler(Action handler) { Requires.NotNull(handler, nameof(handler)); @@ -239,5 +217,24 @@ namespace Microsoft.VisualStudio.Threading return Task.CompletedTask; }; } + + private bool IsJoinableTaskAware( + [NotNullWhen(true)] out JoinableTaskFactory? joinableTaskFactory, + [NotNullWhen(true)] out JoinableTaskCollection? outstandingJoinableTasks, + [NotNullWhen(false)] out object? syncObject, + [NotNullWhen(false)] out HashSet? outstandingTasks, + [NotNullWhen(false)] out TaskFactory? taskFactory) + { + joinableTaskFactory = this.joinableTaskFactory; + outstandingJoinableTasks = this.outstandingJoinableTasks; + + syncObject = this.syncObject; + outstandingTasks = this.outstandingTasks; + taskFactory = this.taskFactory; + +#pragma warning disable CS8762 // Parameter may not have a null value when exiting in some condition. + return joinableTaskFactory is object; +#pragma warning restore CS8762 // Parameter may not have a null value when exiting in some condition. + } } } diff --git a/src/Microsoft.VisualStudio.Threading/Properties/AssemblyInfo.cs b/src/Microsoft.VisualStudio.Threading/Properties/AssemblyInfo.cs index 99e49c94..62399761 100644 --- a/src/Microsoft.VisualStudio.Threading/Properties/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.Threading/Properties/AssemblyInfo.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Resources; using System.Runtime.InteropServices; diff --git a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs index 1f0e2c9d..36cce6b3 100644 --- a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs +++ b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -121,18 +118,6 @@ namespace Microsoft.VisualStudio.Threading } } - /// - /// Gets a value indicating whether this instance is using Joinable Task aware or not. - /// - private bool IsJoinableTaskAware([NotNullWhen(true)] out JoinableTaskFactory? joinableTaskFactory, [NotNullWhen(true)] out JoinableTaskCollection? joinableTaskCollection) - { - joinableTaskFactory = this.joinableTaskFactory; - joinableTaskCollection = this.joinableTaskCollection; -#pragma warning disable CS8762 // Parameter may not have a null value when exiting in some condition. - return this.joinableTaskCollection != null; -#pragma warning restore CS8762 // Parameter may not have a null value when exiting in some condition. - } - /// /// Initializes a new instance of the class. /// @@ -240,6 +225,18 @@ namespace Microsoft.VisualStudio.Threading } } + /// + /// Gets a value indicating whether this instance is using Joinable Task aware or not. + /// + private bool IsJoinableTaskAware([NotNullWhen(true)] out JoinableTaskFactory? joinableTaskFactory, [NotNullWhen(true)] out JoinableTaskCollection? joinableTaskCollection) + { + joinableTaskFactory = this.joinableTaskFactory; + joinableTaskCollection = this.joinableTaskCollection; +#pragma warning disable CS8762 // Parameter may not have a null value when exiting in some condition. + return this.joinableTaskCollection is object; +#pragma warning restore CS8762 // Parameter may not have a null value when exiting in some condition. + } + /// /// Executes the semaphore request. /// @@ -249,7 +246,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(semaphoreUser, nameof(semaphoreUser)); - return this.joinableTaskFactory != null + return this.joinableTaskFactory is object ? this.joinableTaskFactory.RunAsync(semaphoreUser).Task.ConfigureAwaitRunInline() : semaphoreUser().ConfigureAwaitRunInline(); } @@ -263,7 +260,7 @@ namespace Microsoft.VisualStudio.Threading { Requires.NotNull(semaphoreUser, nameof(semaphoreUser)); - return this.joinableTaskFactory != null + return this.joinableTaskFactory is object ? this.joinableTaskFactory.RunAsync(semaphoreUser).Task.ConfigureAwaitRunInline() : semaphoreUser().ConfigureAwaitRunInline(); } @@ -272,7 +269,9 @@ namespace Microsoft.VisualStudio.Threading /// A structure that hides any evidence that the caller has entered a till this value is disposed. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct RevertRelevance : IDisposable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The delegate to invoke on disposal. @@ -333,7 +332,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -343,7 +342,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -357,7 +356,7 @@ namespace Microsoft.VisualStudio.Threading { try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -397,7 +396,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -407,7 +406,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -421,7 +420,7 @@ namespace Microsoft.VisualStudio.Threading { try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -496,7 +495,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -506,7 +505,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -520,7 +519,7 @@ namespace Microsoft.VisualStudio.Threading { try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -546,7 +545,7 @@ namespace Microsoft.VisualStudio.Threading { // Make it clear to any forks of our ExecutionContexxt that the semaphore is no longer owned. // Null check incase the switch to UI thread was cancelled. - if (ownedBox != null) + if (ownedBox is object) { ownedBox.Value = false; } @@ -575,7 +574,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -585,7 +584,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -599,7 +598,7 @@ namespace Microsoft.VisualStudio.Threading { try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -625,7 +624,7 @@ namespace Microsoft.VisualStudio.Threading { // Make it clear to any forks of our ExecutionContexxt that the semaphore is no longer owned. // Null check incase the switch to UI thread was cancelled. - if (ownedBox != null) + if (ownedBox is object) { ownedBox.Value = false; } @@ -638,7 +637,7 @@ namespace Microsoft.VisualStudio.Threading /// public override RevertRelevance SuppressRelevance() { - var originalValue = this.reentrancyDetection.Value; + StrongBox? originalValue = this.reentrancyDetection.Value; this.reentrancyDetection.Value = null; return new RevertRelevance((t, s) => ((NotAllowedSemaphore)t).reentrancyDetection.Value = (StrongBox?)s, this, originalValue); } @@ -683,7 +682,7 @@ namespace Microsoft.VisualStudio.Threading // No race condition here: We're accessing AsyncLocal which we by definition have our own copy of. // Multiple threads or multiple async methods will all have their own storage for this field. Stack>? reentrantStack = this.reentrantCount.Value; - if (reentrantStack == null || reentrantStack.Count == 0) + if (reentrantStack is null || reentrantStack.Count == 0) { // When the stack is empty, the semaphore isn't held. But many execution contexts that forked from a common root // would be sharing this same empty Stack instance. If we pushed to that Stack, all those forks would suddenly @@ -699,7 +698,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -711,7 +710,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -732,7 +731,7 @@ namespace Microsoft.VisualStudio.Threading var pushedReleaser = new StrongBox(releaser); try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -770,7 +769,7 @@ namespace Microsoft.VisualStudio.Threading { lock (reentrantStack) { - var poppedReleaser = reentrantStack.Pop(); + StrongBox? poppedReleaser = reentrantStack.Pop(); if (!object.ReferenceEquals(poppedReleaser, pushedReleaser)) { // When the semaphore faults, we will drain and throw for awaiting tasks one by one. @@ -799,7 +798,7 @@ namespace Microsoft.VisualStudio.Threading // No race condition here: We're accessing AsyncLocal which we by definition have our own copy of. // Multiple threads or multiple async methods will all have their own storage for this field. Stack>? reentrantStack = this.reentrantCount.Value; - if (reentrantStack == null || reentrantStack.Count == 0) + if (reentrantStack is null || reentrantStack.Count == 0) { // When the stack is empty, the semaphore isn't held. But many execution contexts that forked from a common root // would be sharing this same empty Stack instance. If we pushed to that Stack, all those forks would suddenly @@ -815,7 +814,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -827,7 +826,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -848,7 +847,7 @@ namespace Microsoft.VisualStudio.Threading var pushedReleaser = new StrongBox(releaser); try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -886,7 +885,7 @@ namespace Microsoft.VisualStudio.Threading { lock (reentrantStack) { - var poppedReleaser = reentrantStack.Pop(); + StrongBox? poppedReleaser = reentrantStack.Pop(); if (!object.ReferenceEquals(poppedReleaser, pushedReleaser)) { // When the semaphore faults, we will drain and throw for awaiting tasks one by one. @@ -909,7 +908,7 @@ namespace Microsoft.VisualStudio.Threading /// public override RevertRelevance SuppressRelevance() { - var originalValue = this.reentrantCount.Value; + Stack>? originalValue = this.reentrantCount.Value; this.reentrantCount.Value = null; return new RevertRelevance((t, s) => ((StackSemaphore)t).reentrantCount.Value = (Stack>?)s, this, originalValue); } @@ -952,7 +951,7 @@ namespace Microsoft.VisualStudio.Threading // No race condition here: We're accessing AsyncLocal which we by definition have our own copy of. // Multiple threads or multiple async methods will all have their own storage for this field. Stack? reentrantStack = this.reentrantCount.Value; - if (reentrantStack == null || reentrantStack.Count == 0) + if (reentrantStack is null || reentrantStack.Count == 0) { this.reentrantCount.Value = reentrantStack = new Stack(capacity: 2); } @@ -964,7 +963,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -976,7 +975,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -996,7 +995,7 @@ namespace Microsoft.VisualStudio.Threading bool pushed = false; try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -1048,7 +1047,7 @@ namespace Microsoft.VisualStudio.Threading // No race condition here: We're accessing AsyncLocal which we by definition have our own copy of. // Multiple threads or multiple async methods will all have their own storage for this field. Stack? reentrantStack = this.reentrantCount.Value; - if (reentrantStack == null || reentrantStack.Count == 0) + if (reentrantStack is null || reentrantStack.Count == 0) { this.reentrantCount.Value = reentrantStack = new Stack(capacity: 2); } @@ -1060,7 +1059,7 @@ namespace Microsoft.VisualStudio.Threading // resuming on the correct sync context. To partially fix this, we will at least resume you on the main thread or // thread pool. AsyncSemaphore.Releaser releaser; - bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out var joinableTaskCollection) + bool resumeOnMainThread = this.IsJoinableTaskAware(out _, out JoinableTaskCollection? joinableTaskCollection) ? joinableTaskCollection.Context.IsOnMainThread : false; bool mustYield = false; @@ -1072,7 +1071,7 @@ namespace Microsoft.VisualStudio.Threading { // Use ConfiguredAwaitRunInline() as ConfigureAwait(true) will // deadlock due to not being inside a JTF.RunAsync(). - var releaserTask = this.semaphore.EnterAsync(cancellationToken); + Task? releaserTask = this.semaphore.EnterAsync(cancellationToken); mustYield = !releaserTask.IsCompleted; releaser = await releaserTask.ConfigureAwaitRunInline(); } @@ -1092,7 +1091,7 @@ namespace Microsoft.VisualStudio.Threading bool pushed = false; try { - if (this.IsJoinableTaskAware(out var joinableTaskFactory, out _)) + if (this.IsJoinableTaskAware(out JoinableTaskFactory? joinableTaskFactory, out _)) { if (resumeOnMainThread) { @@ -1138,7 +1137,7 @@ namespace Microsoft.VisualStudio.Threading /// public override RevertRelevance SuppressRelevance() { - var originalValue = this.reentrantCount.Value; + Stack? originalValue = this.reentrantCount.Value; this.reentrantCount.Value = null; return new RevertRelevance((t, s) => ((FreeformSemaphore)t).reentrantCount.Value = (Stack?)s, this, originalValue); } diff --git a/src/Microsoft.VisualStudio.Threading/RegistryChangeNotificationFilters.cs b/src/Microsoft.VisualStudio.Threading/RegistryChangeNotificationFilters.cs index 2c216952..0a200667 100644 --- a/src/Microsoft.VisualStudio.Threading/RegistryChangeNotificationFilters.cs +++ b/src/Microsoft.VisualStudio.Threading/RegistryChangeNotificationFilters.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/RoslynDebug.cs b/src/Microsoft.VisualStudio.Threading/RoslynDebug.cs index 6251fefa..e035ecb1 100644 --- a/src/Microsoft.VisualStudio.Threading/RoslynDebug.cs +++ b/src/Microsoft.VisualStudio.Threading/RoslynDebug.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace System.Diagnostics { diff --git a/src/Microsoft.VisualStudio.Threading/SingleThreadedSynchronizationContext.cs b/src/Microsoft.VisualStudio.Threading/SingleThreadedSynchronizationContext.cs index afd2f0c2..645d86b8 100644 --- a/src/Microsoft.VisualStudio.Threading/SingleThreadedSynchronizationContext.cs +++ b/src/Microsoft.VisualStudio.Threading/SingleThreadedSynchronizationContext.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -111,7 +108,7 @@ namespace Microsoft.VisualStudio.Threading } evt.Wait(); - if (caughtException != null) + if (caughtException is object) { throw new TargetInvocationException(caughtException); } @@ -162,7 +159,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (message.Context != null) + if (message.Context is object) { ExecutionContext.Run( message.Context, @@ -198,7 +195,9 @@ namespace Microsoft.VisualStudio.Threading /// A message pumping frame that may be pushed with to pump messages /// on the owning thread. /// +#pragma warning disable CA1034 // Nested types should not be visible public class Frame +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The owning sync context. @@ -223,7 +222,7 @@ namespace Microsoft.VisualStudio.Threading set { - Verify.Operation(this.owner != null, Strings.FrameMustBePushedFirst); + Verify.Operation(this.owner is object, Strings.FrameMustBePushedFirst); this.@continue = value; @@ -243,7 +242,7 @@ namespace Microsoft.VisualStudio.Threading { if (context != this.owner) { - Verify.Operation(this.owner == null, Strings.SyncContextFrameMismatchedAffinity); + Verify.Operation(this.owner is null, Strings.SyncContextFrameMismatchedAffinity); this.owner = context; } } diff --git a/src/Microsoft.VisualStudio.Threading/SpecializedSyncContext.cs b/src/Microsoft.VisualStudio.Threading/SpecializedSyncContext.cs index e23bc00a..a0980abe 100644 --- a/src/Microsoft.VisualStudio.Threading/SpecializedSyncContext.cs +++ b/src/Microsoft.VisualStudio.Threading/SpecializedSyncContext.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining.cs b/src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining`1.cs similarity index 90% rename from src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining.cs rename to src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining`1.cs index 68e88956..2f233cdf 100644 --- a/src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining.cs +++ b/src/Microsoft.VisualStudio.Threading/TaskCompletionSourceWithoutInlining`1.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { diff --git a/src/Microsoft.VisualStudio.Threading/ThreadingEventSource.cs b/src/Microsoft.VisualStudio.Threading/ThreadingEventSource.cs index ef96795f..c6d88e93 100644 --- a/src/Microsoft.VisualStudio.Threading/ThreadingEventSource.cs +++ b/src/Microsoft.VisualStudio.Threading/ThreadingEventSource.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -19,6 +16,11 @@ namespace Microsoft.VisualStudio.Threading [EventSource(Name = "Microsoft-VisualStudio-Threading")] internal sealed partial class ThreadingEventSource : EventSource { + /// + /// The singleton instance used for logging. + /// + internal static readonly ThreadingEventSource Instance = new ThreadingEventSource(); + /// /// The event ID for the event. /// @@ -64,13 +66,6 @@ namespace Microsoft.VisualStudio.Threading /// private const int PostExecutionStopEvent = 16; - /// - /// The singleton instance used for logging. - /// - internal static readonly ThreadingEventSource Instance = new ThreadingEventSource(); - -#region ReaderWriterLock Events - /// /// Logs an issued lock. /// @@ -98,8 +93,6 @@ namespace Microsoft.VisualStudio.Threading this.WriteEvent(WaitReaderWriterLockStopEvent, lockId, kind); } -#endregion - /// /// Enters a synchronously task. /// diff --git a/src/Microsoft.VisualStudio.Threading/ThreadingTools.cs b/src/Microsoft.VisualStudio.Threading/ThreadingTools.cs index 7099079a..5316ed9a 100644 --- a/src/Microsoft.VisualStudio.Threading/ThreadingTools.cs +++ b/src/Microsoft.VisualStudio.Threading/ThreadingTools.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -15,6 +12,11 @@ namespace Microsoft.VisualStudio.Threading /// public static class ThreadingTools { + internal interface ICancellationNotification + { + void OnCanceled(); + } + /// /// Optimistically performs some value transformation based on some field and tries to apply it back to the field, /// retrying as many times as necessary until no other thread is manipulating the same field. @@ -236,7 +238,9 @@ namespace Microsoft.VisualStudio.Threading var t2 = (CancelableTaskCompletionSource)s2!; t2.CancellationTokenRegistration.Dispose(); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { // Swallow any exception. Report.Fail(ex.Message); @@ -310,11 +314,6 @@ namespace Microsoft.VisualStudio.Threading await task.ConfigureAwait(continueOnCapturedContext); } - internal interface ICancellationNotification - { - void OnCanceled(); - } - /// /// A state object for tracking cancellation and a TaskCompletionSource. /// diff --git a/src/Microsoft.VisualStudio.Threading/TplExtensions.cs b/src/Microsoft.VisualStudio.Threading/TplExtensions.cs index 1be31eae..084c6e84 100644 --- a/src/Microsoft.VisualStudio.Threading/TplExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading/TplExtensions.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -55,7 +52,7 @@ namespace Microsoft.VisualStudio.Threading if (!task.IsCompleted) { // Waiting on a continuation of a task won't ever inline the predecessor (in .NET 4.x anyway). - var continuation = task.ContinueWith(t => { }, CancellationToken.None, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); + Task? continuation = task.ContinueWith(t => { }, CancellationToken.None, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); continuation.Wait(); } @@ -216,7 +213,7 @@ namespace Microsoft.VisualStudio.Threading if (ultimateCancellation.CanBeCanceled) { - var registeredCallback = ultimateCancellation.Register( + CancellationTokenRegistration registeredCallback = ultimateCancellation.Register( state => { var tuple = (Tuple, T>, CancellationToken>)state!; @@ -228,7 +225,7 @@ namespace Microsoft.VisualStudio.Threading FollowCancelableTaskToCompletionHelper(tcs, taskToFollow()); - if (taskThatFollows == null) + if (taskThatFollows is null) { return tcs.Task; } @@ -286,9 +283,9 @@ namespace Microsoft.VisualStudio.Threading /// Thrown if any handlers fail. It contains a collection of all failures. public static async Task InvokeAsync(this AsyncEventHandler? handlers, object? sender, EventArgs args) { - if (handlers != null) + if (handlers is object) { - var individualHandlers = handlers.GetInvocationList(); + Delegate[]? individualHandlers = handlers.GetInvocationList(); List? exceptions = null; foreach (AsyncEventHandler handler in individualHandlers) { @@ -296,9 +293,11 @@ namespace Microsoft.VisualStudio.Threading { await handler(sender, args).ConfigureAwait(true); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - if (exceptions == null) + if (exceptions is null) { exceptions = new List(2); } @@ -307,7 +306,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (exceptions != null) + if (exceptions is object) { throw new AggregateException(exceptions); } @@ -326,9 +325,9 @@ namespace Microsoft.VisualStudio.Threading /// Thrown if any handlers fail. It contains a collection of all failures. public static async Task InvokeAsync(this AsyncEventHandler? handlers, object? sender, TEventArgs args) { - if (handlers != null) + if (handlers is object) { - var individualHandlers = handlers.GetInvocationList(); + Delegate[]? individualHandlers = handlers.GetInvocationList(); List? exceptions = null; foreach (AsyncEventHandler handler in individualHandlers) { @@ -336,9 +335,11 @@ namespace Microsoft.VisualStudio.Threading { await handler(sender, args).ConfigureAwait(true); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { - if (exceptions == null) + if (exceptions is null) { exceptions = new List(2); } @@ -347,7 +348,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (exceptions != null) + if (exceptions is object) { throw new AggregateException(exceptions); } @@ -369,7 +370,7 @@ namespace Microsoft.VisualStudio.Threading if (task.AsyncState == state) { - if (callback != null) + if (callback is object) { task.ContinueWith( (t, cb) => ((AsyncCallback)cb!)(t), @@ -411,7 +412,7 @@ namespace Microsoft.VisualStudio.Threading if (task.AsyncState == state) { - if (callback != null) + if (callback is object) { task.ContinueWith( (t, cb) => ((AsyncCallback)cb!)(t), @@ -666,7 +667,7 @@ namespace Microsoft.VisualStudio.Threading tcsNested.SourceState.RegisteredCallback.Dispose(); break; case TaskStatus.Canceled: - var newTask = tcsNested.SourceState.CurrentTask; + Task? newTask = tcsNested.SourceState.CurrentTask; Assumes.True(newTask != t, "A canceled task was not replaced with a new task."); FollowCancelableTaskToCompletionHelper(tcsNested, newTask); break; @@ -680,11 +681,15 @@ namespace Microsoft.VisualStudio.Threading return tcs.Task; } +#pragma warning disable CA1034 // Nested types should not be visible /// /// An awaitable that wraps a task and never throws an exception when waited on. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning restore CA1034 // Nested types should not be visible +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct NoThrowTaskAwaitable +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The task. @@ -719,11 +724,15 @@ namespace Microsoft.VisualStudio.Threading } } - /// +#pragma warning disable CA1034 // Nested types should not be visible +/// /// An awaiter that wraps a task and never throws an exception when waited on. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] +#pragma warning restore CA1034 // Nested types should not be visible +#pragma warning disable CA1034 // Nested types should not be visible public readonly struct NoThrowTaskAwaiter : ICriticalNotifyCompletion +#pragma warning restore CA1034 // Nested types should not be visible { /// /// The task. @@ -837,7 +846,7 @@ namespace Microsoft.VisualStudio.Threading { get { - var task = this.getTaskToFollow(); + Task? task = this.getTaskToFollow(); Assumes.NotNull(task); return task; } @@ -847,6 +856,19 @@ namespace Microsoft.VisualStudio.Threading => new FollowCancelableTaskState(this.getTaskToFollow, registeredCallback, this.UltimateCancellation); } + /// + /// A cache for canceled instances. + /// + /// The type parameter for the returned task. + private static class CanceledTaskOfTCache + { + /// + /// A task that is already canceled. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] + internal static readonly Task CanceledTask = Task.FromCanceled(new CancellationToken(canceled: true)); + } + /// /// A task completion source that contains additional state. /// @@ -871,18 +893,5 @@ namespace Microsoft.VisualStudio.Threading /// internal TState SourceState { get; set; } } - - /// - /// A cache for canceled instances. - /// - /// The type parameter for the returned task. - private static class CanceledTaskOfTCache - { - /// - /// A task that is already canceled. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] - internal static readonly Task CanceledTask = Task.FromCanceled(new CancellationToken(canceled: true)); - } } } diff --git a/src/Microsoft.VisualStudio.Threading/WeakKeyDictionary.cs b/src/Microsoft.VisualStudio.Threading/WeakKeyDictionary`2.cs similarity index 97% rename from src/Microsoft.VisualStudio.Threading/WeakKeyDictionary.cs rename to src/Microsoft.VisualStudio.Threading/WeakKeyDictionary`2.cs index 69b8df43..c3601b9b 100644 --- a/src/Microsoft.VisualStudio.Threading/WeakKeyDictionary.cs +++ b/src/Microsoft.VisualStudio.Threading/WeakKeyDictionary`2.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading { @@ -168,7 +165,7 @@ namespace Microsoft.VisualStudio.Threading } } - if (remove != null) + if (remove is object) { foreach (WeakReference entry in remove) { @@ -218,7 +215,7 @@ namespace Microsoft.VisualStudio.Threading /// internal bool Any() { - foreach (var item in this.dictionary) + foreach (KeyValuePair.WeakReference, TValue> item in this.dictionary) { if (item.Key.IsAlive) { @@ -260,7 +257,7 @@ namespace Microsoft.VisualStudio.Threading while (this.enumerator.MoveNext()) { key = this.enumerator.Current.Key.Target; - if (key != null) + if (key is object) { this.current = new KeyValuePair(key, this.enumerator.Current.Value); return true; @@ -337,7 +334,7 @@ namespace Microsoft.VisualStudio.Threading /// internal bool IsAlive { - get { return this.notSoWeakTarget != null || (this.weakReference?.IsAlive ?? false); } + get { return this.notSoWeakTarget is object || (this.weakReference?.IsAlive ?? false); } } /// @@ -397,7 +394,7 @@ namespace Microsoft.VisualStudio.Threading while (this.enumerator.MoveNext()) { TKey? key = this.enumerator.Current.Key.Target; - if (key != null) + if (key is object) { this.Current = key; return true; diff --git a/src/SosThreadingTools/Commands.cs b/src/SosThreadingTools/Commands.cs index 1e5ac825..3013737e 100644 --- a/src/SosThreadingTools/Commands.cs +++ b/src/SosThreadingTools/Commands.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { @@ -10,13 +11,10 @@ namespace CpsDbg { private const string DumpAsyncCommand = "dumpasync"; - private static Dictionary commandHandlers; - - static Commands() + private static readonly Dictionary CommandHandlers = new Dictionary(StringComparer.OrdinalIgnoreCase) { - commandHandlers = new Dictionary(StringComparer.OrdinalIgnoreCase); - commandHandlers.Add("dumpasync", new DumpAsyncCommand()); - } + { "dumpasync", new DumpAsyncCommand() }, + }; [DllExport(DumpAsyncCommand, CallingConvention.StdCall)] internal static void DumpAsync(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args) @@ -27,13 +25,13 @@ namespace CpsDbg private static void ExecuteCommand(IntPtr client, string command, [MarshalAs(UnmanagedType.LPStr)] string args) { ICommandHandler handler; - if (!commandHandlers.TryGetValue(command, out handler)) + if (!CommandHandlers.TryGetValue(command, out handler)) { return; } DebuggerContext? context = DebuggerContext.GetDebuggerContext(client); - if (context == null) + if (context is null) { return; } @@ -42,7 +40,9 @@ namespace CpsDbg { handler.Execute(context, args); } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Encountered an unhandled exception running '{command}':"); context.Output.WriteLine(ex.ToString()); diff --git a/src/SosThreadingTools/DebuggerContext.cs b/src/SosThreadingTools/DebuggerContext.cs index f7140105..493e3d0e 100644 --- a/src/SosThreadingTools/DebuggerContext.cs +++ b/src/SosThreadingTools/DebuggerContext.cs @@ -1,9 +1,11 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { using System; using System.Diagnostics; + using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -49,14 +51,16 @@ namespace CpsDbg // 1. Store a copy of IDebugClient in DebugClient. // 2. Replace Console's output stream to be the debugger window. // 3. Create an instance of DataTarget using the IDebugClient. - if (instance == null) + if (instance is null) { object client = Marshal.GetUniqueObjectForIUnknown(ptrClient); var debugClient = (IDebugClient)client; var output = new DebuggerOutput(debugClient); +#pragma warning disable CA2000 // Dispose objects before losing scope var dataTarget = DataTarget.CreateFromDbgEng(ptrClient); +#pragma warning restore CA2000 // Dispose objects before losing scope ClrRuntime? runtime = null; @@ -69,7 +73,7 @@ namespace CpsDbg Process p = Process.GetCurrentProcess(); foreach (ProcessModule module in p.Modules) { - if (module.FileName.ToLower().Contains("mscordacwks")) + if (module.FileName.ToUpperInvariant().Contains("MSCORDACWKS")) { // TODO: This does not support side-by-side CLRs. runtime = dataTarget.ClrVersions.Single().CreateRuntime(module.FileName); @@ -78,13 +82,13 @@ namespace CpsDbg } // Otherwise, the user didn't run .cordll. - if (runtime == null) + if (runtime is null) { output.WriteLine("Mscordacwks.dll not loaded into the debugger."); output.WriteLine("Run .cordll to load the dac before running this command."); } - if (runtime != null) + if (runtime is object) { instance = new DebuggerContext(debugClient, dataTarget, runtime, output); } @@ -106,7 +110,7 @@ namespace CpsDbg { string codebase = Assembly.GetExecutingAssembly().CodeBase; - if (codebase.StartsWith("file://")) + if (codebase.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) { codebase = codebase.Substring(8).Replace('/', '\\'); } diff --git a/src/SosThreadingTools/DebuggerOutput.cs b/src/SosThreadingTools/DebuggerOutput.cs index a232f4bc..dcda4ca6 100644 --- a/src/SosThreadingTools/DebuggerOutput.cs +++ b/src/SosThreadingTools/DebuggerOutput.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { diff --git a/src/SosThreadingTools/DumpAsyncCommand.cs b/src/SosThreadingTools/DumpAsyncCommand.cs index b342831c..d96d3b55 100644 --- a/src/SosThreadingTools/DumpAsyncCommand.cs +++ b/src/SosThreadingTools/DumpAsyncCommand.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { @@ -22,20 +23,20 @@ namespace CpsDbg ChainStateMachinesBasedOnTaskContinuations(context, knownStateMachines); ChainStateMachinesBasedOnJointableTasks(context, allStateMachines); MarkThreadingBlockTasks(context, allStateMachines); - MarkUIThreadDependingTasks(context, allStateMachines); + MarkUIThreadDependingTasks(allStateMachines); FixBrokenDependencies(allStateMachines); PrintOutStateMachines(allStateMachines, context.Output); - this.LoadCodePages(context, allStateMachines); + LoadCodePages(context, allStateMachines); } private static void GetAllStateMachines(DebuggerContext context, ClrHeap heap, List allStateMachines, Dictionary knownStateMachines) { - foreach (var obj in heap.GetObjectsOfType("System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner")) + foreach (ClrObject obj in heap.GetObjectsOfType("System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner")) { try { - var stateMachine = obj.ReadObjectField("m_stateMachine"); + ClrObject stateMachine = obj.ReadObjectField("m_stateMachine"); if (!knownStateMachines.ContainsKey(stateMachine.Address)) { try @@ -44,7 +45,7 @@ namespace CpsDbg if (state >= -1) { ClrObject taskField = default(ClrObject); - var asyncBuilder = stateMachine.TryGetValueClassField("<>t__builder"); + ClrValueType? asyncBuilder = stateMachine.TryGetValueClassField("<>t__builder"); if (asyncBuilder.HasValue) { while (asyncBuilder.HasValue) @@ -82,7 +83,7 @@ namespace CpsDbg if (stateMachine.Type is object) { - foreach (var method in stateMachine.Type.Methods) + foreach (ClrMethod? method in stateMachine.Type.Methods) { if (method.Name == "MoveNext" && method.NativeCode != ulong.MaxValue) { @@ -92,13 +93,17 @@ namespace CpsDbg } } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to process state machine {stateMachine.Address:x} Type:'{stateMachine.Type?.Name}' Module:'{stateMachine.Type?.Module?.Name}' Error: {ex.Message}"); } } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to process AsyncStateMachine Runner {obj.Address:x} Error: {ex.Message}"); } @@ -107,9 +112,9 @@ namespace CpsDbg private static void ChainStateMachinesBasedOnTaskContinuations(DebuggerContext context, Dictionary knownStateMachines) { - foreach (var stateMachine in knownStateMachines.Values) + foreach (AsyncStateMachine? stateMachine in knownStateMachines.Values) { - var taskObject = stateMachine.Task; + ClrObject taskObject = stateMachine.Task; try { while (!taskObject.IsNull) @@ -118,7 +123,7 @@ namespace CpsDbg // 1. m_continuationObject.m_action._target // 2. m_continuationObject._target // 3. m_continuationObject.m_task.m_stateObject._target - var continuationObject = taskObject.TryGetObjectField("m_continuationObject"); + ClrObject continuationObject = taskObject.TryGetObjectField("m_continuationObject"); if (continuationObject.IsNull) { break; @@ -129,7 +134,9 @@ namespace CpsDbg taskObject = continuationObject; } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}"); } @@ -138,10 +145,10 @@ namespace CpsDbg private static void ChainStateMachineBasedOnTaskContinuations(Dictionary knownStateMachines, AsyncStateMachine stateMachine, ClrObject continuationObject) { - var continuationAction = continuationObject.TryGetObjectField("m_action"); + ClrObject continuationAction = continuationObject.TryGetObjectField("m_action"); // case 1 - var continuationTarget = continuationAction.TryGetObjectField("_target"); + ClrObject continuationTarget = continuationAction.TryGetObjectField("_target"); if (continuationTarget.IsNull) { // case 2 @@ -156,7 +163,7 @@ namespace CpsDbg while (!continuationTarget.IsNull) { // now get the continuation from the target - var continuationTargetStateMachine = continuationTarget.TryGetObjectField("m_stateMachine"); + ClrObject continuationTargetStateMachine = continuationTarget.TryGetObjectField("m_stateMachine"); if (!continuationTargetStateMachine.IsNull) { AsyncStateMachine targetAsyncState; @@ -171,19 +178,19 @@ namespace CpsDbg } else { - var nextContinuation = continuationTarget.TryGetObjectField("m_continuation"); + ClrObject nextContinuation = continuationTarget.TryGetObjectField("m_continuation"); continuationTarget = nextContinuation.TryGetObjectField("_target"); } } - var items = continuationObject.TryGetObjectField("_items"); + ClrObject items = continuationObject.TryGetObjectField("_items"); if (!items.IsNull && items.IsArray && items.ContainsPointers) { - foreach (var promise in items.EnumerateReferences(true)) + foreach (ClrObject promise in items.EnumerateReferences(true)) { if (!promise.IsNull) { - var innerContinuationObject = promise.TryGetObjectField("m_continuationObject"); + ClrObject innerContinuationObject = promise.TryGetObjectField("m_continuationObject"); if (!innerContinuationObject.IsNull) { ChainStateMachineBasedOnTaskContinuations(knownStateMachines, stateMachine, innerContinuationObject); @@ -199,17 +206,17 @@ namespace CpsDbg private static void ChainStateMachinesBasedOnJointableTasks(DebuggerContext context, List allStateMachines) { - foreach (var stateMachine in allStateMachines) + foreach (AsyncStateMachine? stateMachine in allStateMachines) { if (stateMachine.Previous is null) { try { - var joinableTask = stateMachine.StateMachine.TryGetObjectField("<>4__this"); - var wrappedTask = joinableTask.TryGetObjectField("wrappedTask"); + ClrObject joinableTask = stateMachine.StateMachine.TryGetObjectField("<>4__this"); + ClrObject wrappedTask = joinableTask.TryGetObjectField("wrappedTask"); if (!wrappedTask.IsNull) { - var previousStateMachine = allStateMachines + AsyncStateMachine? previousStateMachine = allStateMachines .FirstOrDefault(s => s.Task.Address == wrappedTask.Address); if (previousStateMachine is object && stateMachine != previousStateMachine) { @@ -219,7 +226,9 @@ namespace CpsDbg } } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types { context.Output.WriteLine($"Fail to fix continuation of state {stateMachine.StateMachine.Address:x} Error: {ex.Message}"); } @@ -229,9 +238,9 @@ namespace CpsDbg private static void MarkThreadingBlockTasks(DebuggerContext context, List allStateMachines) { - foreach (var thread in context.Runtime.Threads) + foreach (ClrThread? thread in context.Runtime.Threads) { - var stackFrame = thread.EnumerateStackTrace().Take(50).FirstOrDefault( + ClrStackFrame? stackFrame = thread.EnumerateStackTrace().Take(50).FirstOrDefault( f => f.Method is { } method && string.Equals(f.Method.Name, "CompleteOnCurrentThread", StringComparison.Ordinal) && string.Equals(f.Method.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTask", StringComparison.Ordinal)); @@ -252,10 +261,10 @@ namespace CpsDbg if ((state & 0x10) == 0x10) { // This flag indicates the JTF is blocking the thread - var wrappedTask = joinableTaskObject.TryGetObjectField("wrappedTask"); + ClrObject wrappedTask = joinableTaskObject.TryGetObjectField("wrappedTask"); if (!wrappedTask.IsNull) { - var blockingStateMachine = allStateMachines + AsyncStateMachine? blockingStateMachine = allStateMachines .FirstOrDefault(s => s.Task.Address == wrappedTask.Address); if (blockingStateMachine is object) { @@ -273,25 +282,27 @@ namespace CpsDbg } } - private static void MarkUIThreadDependingTasks(DebuggerContext context, List allStateMachines) + private static void MarkUIThreadDependingTasks(List allStateMachines) { - foreach (var stateMachine in allStateMachines) + foreach (AsyncStateMachine? stateMachine in allStateMachines) { if (stateMachine.Previous is null && stateMachine.State >= 0) { try { - var awaitField = stateMachine.StateMachine.Type?.GetFieldByName($"<>u__{stateMachine.State + 1}"); - if (awaitField is object && awaitField.IsValueType && string.Equals(awaitField.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTaskFactory+MainThreadAwaiter")) + ClrInstanceField? awaitField = stateMachine.StateMachine.Type?.GetFieldByName($"<>u__{stateMachine.State + 1}"); + if (awaitField is object && awaitField.IsValueType && string.Equals(awaitField.Type?.Name, "Microsoft.VisualStudio.Threading.JoinableTaskFactory+MainThreadAwaiter", StringComparison.Ordinal)) { - var awaitObject = stateMachine.StateMachine.TryGetValueClassField($"<>u__{stateMachine.State + 1}"); + ClrValueType? awaitObject = stateMachine.StateMachine.TryGetValueClassField($"<>u__{stateMachine.State + 1}"); if (awaitObject.HasValue) { stateMachine.SwitchToMainThreadTask = awaitObject.TryGetObjectField("job"); } } } +#pragma warning disable CA1031 // Do not catch general exception types catch (Exception) +#pragma warning restore CA1031 // Do not catch general exception types { } } @@ -300,7 +311,7 @@ namespace CpsDbg private static void FixBrokenDependencies(List allStateMachines) { - foreach (var stateMachine in allStateMachines) + foreach (AsyncStateMachine? stateMachine in allStateMachines) { if (stateMachine.Previous is object && stateMachine.Previous.Next != stateMachine) { @@ -314,7 +325,7 @@ namespace CpsDbg private static void PrintOutStateMachines(List allStateMachines, DebuggerOutput output) { int loopMark = -1; - foreach (var stateMachine in allStateMachines) + foreach (AsyncStateMachine? stateMachine in allStateMachines) { int depth = 0; if (stateMachine.Previous is null) @@ -344,7 +355,7 @@ namespace CpsDbg var printedMachines = new HashSet(); - foreach (var node in allStateMachines + foreach (AsyncStateMachine? node in allStateMachines .Where(m => m.Depth > 0) .OrderByDescending(m => m.Depth) .ThenByDescending(m => m.SwitchToMainThreadTask.Address)) @@ -361,7 +372,7 @@ namespace CpsDbg if (allStateMachines.Count > printedMachines.Count) { output.WriteLine("States form dependencies loop -- could be an error caused by the analysis tool"); - foreach (var node in allStateMachines) + foreach (AsyncStateMachine? node in allStateMachines) { if (!printedMachines.Contains(node)) { @@ -442,10 +453,10 @@ namespace CpsDbg return multipleLineBlock; } - private void LoadCodePages(DebuggerContext context, List allStateMachines) + private static void LoadCodePages(DebuggerContext context, List allStateMachines) { var loadedAddresses = new HashSet(); - foreach (var stateMachine in allStateMachines) + foreach (AsyncStateMachine? stateMachine in allStateMachines) { ulong codeAddress = stateMachine.CodeAddress; if (loadedAddresses.Add(codeAddress)) diff --git a/src/SosThreadingTools/ExtensionContext.cs b/src/SosThreadingTools/ExtensionContext.cs index 80dcfdbc..8e6beb41 100644 --- a/src/SosThreadingTools/ExtensionContext.cs +++ b/src/SosThreadingTools/ExtensionContext.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { diff --git a/src/SosThreadingTools/ICommandHandler.cs b/src/SosThreadingTools/ICommandHandler.cs index e7debaaa..e976975f 100644 --- a/src/SosThreadingTools/ICommandHandler.cs +++ b/src/SosThreadingTools/ICommandHandler.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { diff --git a/src/SosThreadingTools/SosThreadingTools.csproj b/src/SosThreadingTools/SosThreadingTools.csproj index 91769adc..080fb03e 100644 --- a/src/SosThreadingTools/SosThreadingTools.csproj +++ b/src/SosThreadingTools/SosThreadingTools.csproj @@ -5,8 +5,6 @@ true $(MSBuildProjectName) - SosThreadingTools.ruleset - true true false diff --git a/src/SosThreadingTools/SosThreadingTools.ruleset b/src/SosThreadingTools/SosThreadingTools.ruleset deleted file mode 100644 index 6cb2d954..00000000 --- a/src/SosThreadingTools/SosThreadingTools.ruleset +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/SosThreadingTools/Utilities.cs b/src/SosThreadingTools/Utilities.cs index 336461e1..1391cc7d 100644 --- a/src/SosThreadingTools/Utilities.cs +++ b/src/SosThreadingTools/Utilities.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace CpsDbg { @@ -13,7 +14,7 @@ namespace CpsDbg { if (!clrObject.IsNull) { - var field = clrObject.Type?.GetFieldByName(fieldName); + ClrInstanceField? field = clrObject.Type?.GetFieldByName(fieldName); if (field is object && field.IsObjectReference) { return field.ReadObject(clrObject.Address, interior: false); @@ -27,7 +28,7 @@ namespace CpsDbg { if (!clrObject.IsNull) { - var field = clrObject.Type?.GetFieldByName(fieldName); + ClrInstanceField? field = clrObject.Type?.GetFieldByName(fieldName); if (field?.Type is object && field.Type.IsValueType) { // System.Console.WriteLine("{0} {1:x} Field {2} {3} {4} {5}", clrObject.Type.Name, clrObject.Address, fieldName, field.Type.Name, field.Type.IsValueType, field.Type.IsRuntimeType); @@ -42,7 +43,7 @@ namespace CpsDbg { if (clrObject is object) { - var field = clrObject.Value.Type?.GetFieldByName(fieldName); + ClrInstanceField? field = clrObject.Value.Type?.GetFieldByName(fieldName); if (field is object && field.IsObjectReference) { return clrObject.Value.ReadObjectField(fieldName); @@ -56,7 +57,7 @@ namespace CpsDbg { if (clrObject.HasValue) { - var field = clrObject.Value.Type?.GetFieldByName(fieldName); + ClrInstanceField? field = clrObject.Value.Type?.GetFieldByName(fieldName); if (field is object && field.IsValueType) { return clrObject.Value.ReadValueTypeField(fieldName); diff --git a/src/stylecop.json b/src/stylecop.json deleted file mode 100644 index 5a6f3b20..00000000 --- a/src/stylecop.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "Microsoft Corporation", - "xmlHeader": false, - "fileNamingConvention": "metadata" - } - } -} \ No newline at end of file diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 00000000..6c045a1a --- /dev/null +++ b/stylecop.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Microsoft", + "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", + "variables": { + "licenseName": "MIT", + "licenseFile": "LICENSE" + }, + "xmlHeader": false, + "fileNamingConvention": "metadata" + } + } +} diff --git a/test/.editorconfig b/test/.editorconfig new file mode 100644 index 00000000..f9828ec4 --- /dev/null +++ b/test/.editorconfig @@ -0,0 +1,73 @@ +[*.cs] + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = silent + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = silent + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = silent + +# SA1615: Element return value should be documented +dotnet_diagnostic.SA1615.severity = silent + +# VSTHRD103: Call async methods when in an async method +dotnet_diagnostic.VSTHRD103.severity = silent + +# VSTHRD111: Use .ConfigureAwait(bool) +dotnet_diagnostic.VSTHRD111.severity = none + +# VSTHRD200: Use Async suffix for async methods +dotnet_diagnostic.VSTHRD200.severity = silent + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = silent + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = silent + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = suggestion + +# CA1063: Implement IDisposable Correctly +dotnet_diagnostic.CA1063.severity = silent + +# CA1816: Dispose methods should call SuppressFinalize +dotnet_diagnostic.CA1816.severity = silent + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = none + +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = suggestion + +# CA1051: Do not declare visible instance fields +dotnet_diagnostic.CA1051.severity = none + +# CA1064: Exceptions should be public +dotnet_diagnostic.CA1064.severity = none + +# Default severity for analyzer diagnostics with category 'Design' +dotnet_analyzer_diagnostic.category-Design.severity = silent + +# Default severity for analyzer diagnostics with category 'Performance' +dotnet_analyzer_diagnostic.category-Performance.severity = suggestion + +# Default severity for analyzer diagnostics with category 'Reliability' +dotnet_analyzer_diagnostic.category-Reliability.severity = suggestion + +# CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1001.severity = suggestion + +# CA1307: Specify StringComparison +dotnet_diagnostic.CA1307.severity = suggestion + +# SA1604: Element documentation should have summary +dotnet_diagnostic.SA1604.severity = suggestion + +# SA1401: Fields should be private +dotnet_diagnostic.SA1401.severity = none diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 00000000..87772b5a --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,13 @@ + + + + + false + true + + + + + + + diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets new file mode 100644 index 00000000..2faab375 --- /dev/null +++ b/test/Directory.Build.targets @@ -0,0 +1,10 @@ + + + cobertura + [xunit.*]* + + $(OutputPath)/ + + + + diff --git a/src/IsolatedTestHost/App.config b/test/IsolatedTestHost/App.config similarity index 100% rename from src/IsolatedTestHost/App.config rename to test/IsolatedTestHost/App.config diff --git a/src/IsolatedTestHost/ExitCodes.cs b/test/IsolatedTestHost/ExitCode.cs similarity index 78% rename from src/IsolatedTestHost/ExitCodes.cs rename to test/IsolatedTestHost/ExitCode.cs index 27f5b41b..ebad5684 100644 --- a/src/IsolatedTestHost/ExitCodes.cs +++ b/test/IsolatedTestHost/ExitCode.cs @@ -1,15 +1,12 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace IsolatedTestHost { /// /// The meanings of each exit code that may be returned from this process. /// - public enum ExitCodes + public enum ExitCode { /// /// The test executed and passed. diff --git a/test/IsolatedTestHost/IsolatedTestHost.csproj b/test/IsolatedTestHost/IsolatedTestHost.csproj new file mode 100644 index 00000000..a3463778 --- /dev/null +++ b/test/IsolatedTestHost/IsolatedTestHost.csproj @@ -0,0 +1,12 @@ + + + + Exe + net472 + + + + + + + diff --git a/src/IsolatedTestHost/Program.cs b/test/IsolatedTestHost/Program.cs similarity index 71% rename from src/IsolatedTestHost/Program.cs rename to test/IsolatedTestHost/Program.cs index f1d7354e..b6d9a6a4 100644 --- a/src/IsolatedTestHost/Program.cs +++ b/test/IsolatedTestHost/Program.cs @@ -1,4 +1,7 @@ -namespace IsolatedTestHost +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace IsolatedTestHost { using System; using System.IO; @@ -14,7 +17,7 @@ { if (args.Length != 3) { - return (int)ExitCodes.UnexpectedCommandLineArgs; + return (int)ExitCode.UnexpectedCommandLineArgs; } string assemblyFile = args[0]; @@ -24,7 +27,7 @@ return (int)MyMain(assemblyFile, testClassName, testMethodName); } - private static ExitCodes MyMain(string assemblyFile, string testClassName, string testMethodName) + private static ExitCode MyMain(string assemblyFile, string testClassName, string testMethodName) { Assembly assembly; try @@ -34,19 +37,19 @@ catch (FileNotFoundException ex) { Console.Error.WriteLine(ex.Message); - return ExitCodes.AssemblyNotFound; + return ExitCode.AssemblyNotFound; } Type testClass = assembly.GetType(testClassName); - if (testClass == null) + if (testClass is null) { - return ExitCodes.TestClassNotFound; + return ExitCode.TestClassNotFound; } MethodInfo testMethod = testClass.GetRuntimeMethod(testMethodName, Type.EmptyTypes); - if (testMethod == null) + if (testMethod is null) { - return ExitCodes.TestMethodNotFound; + return ExitCode.TestMethodNotFound; } bool fact = testMethod.GetCustomAttributesData().Any(a => a.AttributeType.Name == "FactAttribute"); @@ -59,7 +62,7 @@ bool stafact = testMethod.GetCustomAttributesData().Any(a => a.AttributeType.Name == "StaFactAttribute"); if (stafact) { - ExitCodes result = ExitCodes.TestFailed; + ExitCode result = ExitCode.TestFailed; var testThread = new Thread(() => { result = ExecuteTest(testClass, testMethod); @@ -70,22 +73,22 @@ return result; } - return ExitCodes.TestNotSupported; + return ExitCode.TestNotSupported; } - private static ExitCodes ExecuteTest(Type testClass, MethodInfo testMethod) + private static ExitCode ExecuteTest(Type testClass, MethodInfo testMethod) { try { - var ctorWithLogger = testClass.GetConstructors().FirstOrDefault( + ConstructorInfo? ctorWithLogger = testClass.GetConstructors().FirstOrDefault( ctor => ctor.GetParameters().Length == 1 && ctor.GetParameters()[0].ParameterType.IsAssignableFrom(typeof(TestOutputHelper))); - var ctorDefault = testClass.GetConstructor(Type.EmptyTypes); + ConstructorInfo? ctorDefault = testClass.GetConstructor(Type.EmptyTypes); object? testClassInstance = ctorWithLogger?.Invoke(new object[] { new TestOutputHelper() }) ?? ctorDefault?.Invoke(Type.EmptyTypes); - if (testClassInstance == null) + if (testClassInstance is null) { - return ExitCodes.TestNotSupported; + return ExitCode.TestNotSupported; } object result = testMethod.Invoke(testClassInstance, Type.EmptyTypes); @@ -99,18 +102,18 @@ disposableTestClass.Dispose(); } - return ExitCodes.TestPassed; + return ExitCode.TestPassed; } catch (Exception ex) { if (ex.GetType().Name == "SkipException") { - return ExitCodes.TestSkipped; + return ExitCode.TestSkipped; } Console.Error.WriteLine("Test failed."); Console.Error.WriteLine(ex); - return ExitCodes.TestFailed; + return ExitCode.TestFailed; } } } diff --git a/src/IsolatedTestHost/TestOutputHelper.cs b/test/IsolatedTestHost/TestOutputHelper.cs similarity index 66% rename from src/IsolatedTestHost/TestOutputHelper.cs rename to test/IsolatedTestHost/TestOutputHelper.cs index f997e8f1..dc2ab263 100644 --- a/src/IsolatedTestHost/TestOutputHelper.cs +++ b/test/IsolatedTestHost/TestOutputHelper.cs @@ -1,4 +1,7 @@ -namespace IsolatedTestHost +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace IsolatedTestHost { using System; using Xunit.Abstractions; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.mocks.txt b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.mocks.txt similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.mocks.txt rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.LegacyThreadSwitchingMembers.mocks.txt diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadAssertingMethods.mocks.txt b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadAssertingMethods.mocks.txt similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadAssertingMethods.mocks.txt rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadAssertingMethods.mocks.txt diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.mocks.txt b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.mocks.txt similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.mocks.txt rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MainThreadSwitchingMethods.mocks.txt diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MembersRequiringMainThread.mocks.txt b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MembersRequiringMainThread.mocks.txt similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MembersRequiringMainThread.mocks.txt rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/AdditionalFiles/vs-threading.MembersRequiringMainThread.mocks.txt diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs similarity index 94% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs index 0c81c5f3..0af9e9c8 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { @@ -71,7 +72,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests }); } - public bool HasEntryPoint { get; set; } = false; + public bool HasEntryPoint { get; set; } public bool IncludeMicrosoftVisualStudioThreading { get; set; } = true; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs index db1cea7f..894ba2f8 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs similarity index 60% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs index d3858351..62e65c9b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/ReferencesHelper.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { @@ -14,13 +15,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests new PackageIdentity("System.Threading.Tasks.Extensions", "4.5.3"), new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "1.1.0"))); - internal static readonly ImmutableArray VSSDKPackageReferences = ImmutableArray.Create(new string[] { - "Microsoft.VisualStudio.Shell.Interop.dll", - "Microsoft.VisualStudio.Shell.Interop.11.0.dll", - "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll", - "Microsoft.VisualStudio.Shell.Immutable.14.0.dll", - "Microsoft.VisualStudio.Shell.14.0.dll", - }); + internal static readonly ImmutableArray VSSDKPackageReferences = ImmutableArray.Create(new string[] + { + "Microsoft.VisualStudio.Shell.Interop.dll", + "Microsoft.VisualStudio.Shell.Interop.11.0.dll", + "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll", + "Microsoft.VisualStudio.Shell.Immutable.14.0.dll", + "Microsoft.VisualStudio.Shell.14.0.dll", + }); static ReferencesHelper() { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs index 20139e51..94287b2d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2+Test.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { @@ -19,7 +20,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { public class Test : VisualBasicCodeFixTest { - private static readonly ImmutableArray VSSDKPackageReferences = ImmutableArray.Create(new string[] { + private static readonly ImmutableArray VSSDKPackageReferences = ImmutableArray.Create(new string[] + { "Microsoft.VisualStudio.Shell.Interop.dll", "Microsoft.VisualStudio.Shell.Interop.11.0.dll", "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll", @@ -77,7 +79,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests }); } - public bool HasEntryPoint { get; set; } = false; + public bool HasEntryPoint { get; set; } public bool IncludeMicrosoftVisualStudioThreading { get; set; } = true; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs index 2c7b9f64..fcff374a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/VisualBasicCodeFixVerifier`2.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj similarity index 71% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj index bcf146d3..843f139d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj @@ -1,10 +1,6 @@  net472 - true - Microsoft.VisualStudio.Threading.Analyzers.Tests.ruleset - $(NoWarn);CS1591 - false true true true @@ -13,11 +9,11 @@ - - - - - + + + + + @@ -40,7 +36,7 @@ - + AdditionalFiles.%(FileName)%(Extension) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs index dd319b01..b9a77dc4 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System; using System.Collections.Generic; @@ -85,7 +88,7 @@ internal class Child : Parent { } "; - var expected = Verify.CompilerError("CS0535").WithLocation(6, 23).WithArguments("Parent", "A.Foo()"); + DiagnosticResult expected = Verify.CompilerError("CS0535").WithLocation(6, 23).WithArguments("Parent", "A.Foo()"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -276,7 +279,7 @@ public class Test { [Fact] public void NoValueTupleReference() { - var refAssemblies = typeof(CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer) + System.Reflection.AssemblyName[]? refAssemblies = typeof(CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer) .Assembly.GetReferencedAssemblies(); Assert.DoesNotContain(refAssemblies, a => a.Name.Equals("System.ValueTuple", StringComparison.OrdinalIgnoreCase)); } @@ -293,7 +296,7 @@ public class Test { [Fact] public void NoValueTaskReference() { - var refAssemblies = typeof(CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer) + System.Reflection.AssemblyName[]? refAssemblies = typeof(CSharpVSTHRD001UseSwitchToMainThreadAsyncAnalyzer) .Assembly.GetReferencedAssemblies(); Assert.DoesNotContain(refAssemblies, a => a.Name.Equals("System.Threading.Tasks.Extensions", StringComparison.OrdinalIgnoreCase)); } @@ -331,7 +334,7 @@ class Foo { } { protected override IEnumerable GetDiagnosticAnalyzers() { - var analyzers = from type in typeof(VSTHRD002UseJtfRunAnalyzer).Assembly.GetTypes() + IEnumerable? analyzers = from type in typeof(VSTHRD002UseJtfRunAnalyzer).Assembly.GetTypes() where type.GetCustomAttributes(typeof(DiagnosticAnalyzerAttribute), true).Any() select (DiagnosticAnalyzer)Activator.CreateInstance(type); return analyzers.ToImmutableArray(); diff --git a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5a990af9 --- /dev/null +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs index 44b83625..2aefb06c 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD001UseSwitchToMainThreadAsyncAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs index b96367f8..50a55edc 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -38,7 +41,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 14, 8, 18); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 14, 8, 18); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -69,7 +72,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(9, 14, 9, 21); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 14, 9, 21); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -100,7 +103,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(9, 14, 9, 21); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 14, 9, 21); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -118,7 +121,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 31, 8, 35); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 31, 8, 35); await Verify.VerifyCodeFixAsync(test, expected, test); } @@ -147,7 +150,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 27, 8, 33); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 27, 8, 33); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -176,7 +179,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 27, 8, 33); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 27, 8, 33); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -194,7 +197,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 34, 8, 40); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 34, 8, 40); await Verify.VerifyCodeFixAsync(test, expected, test); } @@ -257,7 +260,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 27, 8, 36); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 27, 8, 36); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -286,14 +289,15 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithSpan(8, 27, 8, 36); + DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 27, 8, 36); await Verify.VerifyCodeFixAsync(test, expected, withFix); } [Fact] public async Task TaskResult_FixUpdatesCallers() { - var test = new SourceFileList("Test", "cs") { + var test = new SourceFileList("Test", "cs") + { @" using System; using System.Threading.Tasks; @@ -324,8 +328,10 @@ class TestClient { return Test.GetNumber(a) * b; } } -", }; - var withFix = new SourceFileList("Test", "cs") { +", + }; + var withFix = new SourceFileList("Test", "cs") + { @" using System; using System.Threading.Tasks; @@ -356,7 +362,8 @@ class TestClient { return await Test.GetNumberAsync(a) * b; } } -", }; +", + }; var verifyTest = new Verify.Test { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs index c640d0d5..01be1e21 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Diagnostics.Tracing; using System.Linq; @@ -39,7 +42,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -71,7 +74,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -91,7 +94,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(10, 59); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(10, 59); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -111,7 +114,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(10, 68); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(10, 68); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -131,7 +134,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(10, 16, 4); + DiagnosticResult expected = this.CreateDiagnostic(10, 16, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -148,7 +151,7 @@ class Tests public Task GetTask() => task; } "; - var expected = this.CreateDiagnostic(8, 30, 4); + DiagnosticResult expected = this.CreateDiagnostic(8, 30, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -163,7 +166,7 @@ class Tests public Task GetTask(Task task) => task; } "; - var expected = this.CreateDiagnostic(6, 39, 4); + DiagnosticResult expected = this.CreateDiagnostic(6, 39, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -183,7 +186,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(10, 22, 4); + DiagnosticResult expected = this.CreateDiagnostic(10, 22, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -205,7 +208,7 @@ class Tests }} }} "; - var expected = this.CreateDiagnostic(10, 15, 21 + continueOnCapturedContext.ToString().Length); + DiagnosticResult expected = this.CreateDiagnostic(10, 15, 21 + continueOnCapturedContext.ToString().Length); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -227,7 +230,7 @@ class Tests }} }} "; - var expected = this.CreateDiagnostic(10, 22, 21 + continueOnCapturedContext.ToString().Length); + DiagnosticResult expected = this.CreateDiagnostic(10, 22, 21 + continueOnCapturedContext.ToString().Length); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -248,7 +251,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(11, 15, 30); + DiagnosticResult expected = this.CreateDiagnostic(11, 15, 30); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -269,7 +272,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(11, 22, 30); + DiagnosticResult expected = this.CreateDiagnostic(11, 22, 30); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -294,7 +297,7 @@ class Program } } "; - var expected = this.CreateDiagnostic(14, 19, 1); + DiagnosticResult expected = this.CreateDiagnostic(14, 19, 1); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -315,7 +318,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(11, 59); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(11, 59); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -362,7 +365,7 @@ class Tests private static Task DoSomethingWith(Task t) => null; } "; - var expected = this.CreateDiagnostic(12, 68, 4); + DiagnosticResult expected = this.CreateDiagnostic(12, 68, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -394,7 +397,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -427,7 +430,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -667,7 +670,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -701,7 +704,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(16, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(16, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -736,7 +739,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(16, 23); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(16, 23); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -900,7 +903,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(17, 23); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(17, 23); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -942,7 +945,7 @@ class Tests } } "; - var expected = Verify.Diagnostic().WithLocation(24, 19); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(24, 19); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -965,7 +968,7 @@ class Tests { } } "; - var expected = this.CreateDiagnostic(12, 19, 4); + DiagnosticResult expected = this.CreateDiagnostic(12, 19, 4); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -988,7 +991,7 @@ class Tests { } } "; - var expected = this.CreateDiagnostic(12, 19, 9); + DiagnosticResult expected = this.CreateDiagnostic(12, 19, 9); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1114,7 +1117,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(10, 16, 13); + DiagnosticResult expected = this.CreateDiagnostic(10, 16, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1134,7 +1137,7 @@ class Tests } } "; - var expected = this.CreateDiagnostic(10, 16, 8); + DiagnosticResult expected = this.CreateDiagnostic(10, 16, 8); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1206,7 +1209,7 @@ public static class Boom { { (solution, projectId) => { - var projectA = solution.AddProject("ProjectA", "ProjectA", LanguageNames.CSharp) + Project? projectA = solution.AddProject("ProjectA", "ProjectA", LanguageNames.CSharp) .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .WithMetadataReferences(solution.GetProject(projectId)!.MetadataReferences.Concat(test!.TestState.AdditionalReferences)) .AddDocument("SpecialTasks.cs", specialTasksCs).Project; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs index 09aa2c1b..599c1bff 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD004AwaitSwitchToMainThreadAsyncAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs similarity index 90% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs index 95923640..25f67047 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -23,7 +26,7 @@ class Test { string name = G.Ref1.Name; } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(10, 26, 10, 30).WithArguments("IVsReference", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(10, 26, 10, 30).WithArguments("IVsReference", "Test.VerifyOnUIThread"); await Verify.VerifyCodeFixAsync(test, expected, test); } @@ -55,7 +58,7 @@ class Test { IVsSolution Method() { return null; } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 23, 7, 34).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 23, 7, 34).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -90,7 +93,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -175,7 +178,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(9, 17, 9, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(9, 17, 9, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -208,7 +211,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 13, 7, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 13, 7, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -280,7 +283,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 9, 7, 12).WithArguments("Test.Foo", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 9, 7, 12).WithArguments("Test.Foo", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -384,7 +387,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(12, 13, 12, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(12, 13, 12, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -409,7 +412,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(11, 17, 11, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(11, 17, 11, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -434,7 +437,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(12, 13, 12, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(12, 13, 12, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -461,7 +464,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorAsync).WithSpan(14, 13, 14, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorAsync).WithSpan(14, 13, 14, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -490,7 +493,7 @@ class Test { async Task SomeAsync() => await Task.Yield(); } "; - var expected = Verify.Diagnostic(DescriptorAsync).WithSpan(14, 13, 14, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorAsync).WithSpan(14, 13, 14, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -520,7 +523,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorAsync).WithSpan(16, 13, 16, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorAsync).WithSpan(16, 13, 16, 24).WithArguments("IVsSolution", "JoinableTaskFactory.SwitchToMainThreadAsync"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -695,7 +698,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -934,7 +937,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 22, 8, 26).WithArguments("IVsReference", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 22, 8, 26).WithArguments("IVsReference", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -952,7 +955,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithLocation(8, 19).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithLocation(8, 19).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1016,7 +1019,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 24, 8, 38).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 24, 8, 38).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1035,7 +1038,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 18, 8, 32).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 18, 8, 32).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1078,7 +1081,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 27, 8, 41).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 27, 8, 41).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1199,7 +1202,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(9, 31, 9, 85).WithArguments("IServiceProvider", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(9, 31, 9, 85).WithArguments("IServiceProvider", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1246,7 +1249,7 @@ class Test : Package { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 29, 8, 39).WithArguments("Package", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 29, 8, 39).WithArguments("Package", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1366,7 +1369,7 @@ class Test : AsyncPackage { } } "; - var expected = Verify.Diagnostic(DescriptorAsync).WithSpan(12, 65, 12, 76).WithArguments("IVsShell", "JoinableTaskFactory.SwitchToMainThreadAsync"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorAsync).WithSpan(12, 65, 12, 76).WithArguments("IVsShell", "JoinableTaskFactory.SwitchToMainThreadAsync"); await new Verify.Test { TestCode = test, @@ -1415,7 +1418,7 @@ class Test : AsyncPackage { " #pragma warning restore CS0219 // Variable is assigned but its value is never used ; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(13, 59, 13, 70).WithArguments("IVsShell", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(13, 59, 13, 70).WithArguments("IVsShell", "Test.VerifyOnUIThread"); await Verify.VerifyCodeFixAsync(test, expected, test); // till we have it implemented. ////await Verify.VerifyCodeFixAsync(test, expected, fix); } @@ -1539,7 +1542,7 @@ class Test : Microsoft.VisualStudio.OLE.Interop.IServiceProvider { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(15, 14, 15, 26).WithArguments("IServiceProvider", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(15, 14, 15, 26).WithArguments("IServiceProvider", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1600,7 +1603,7 @@ namespace TestNS2 } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(13, 18, 13, 41).WithArguments("A", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(13, 18, 13, 41).WithArguments("A", "Test.VerifyOnUIThread"); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1643,7 +1646,7 @@ class A IVsSolution solution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 74, 7, 88).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 74, 7, 88).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyCodeFixAsync(test, expected, test); // the fix (if ever implemented) will be to move the initializer to a ctor, after a thread check. } @@ -1659,7 +1662,7 @@ class A static IVsSolution solution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution; } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 81, 7, 95).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(7, 81, 7, 95).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await Verify.VerifyCodeFixAsync(test, expected, test); } @@ -1690,7 +1693,7 @@ class A }; } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 90, 8, 104).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 90, 8, 104).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -1735,7 +1738,7 @@ class A public static bool operator !=(A item1, A item2) => !(item1 == item2); } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(10, 63, 10, 77).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(10, 63, 10, 77).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -1774,7 +1777,7 @@ class A void Bar(IVsSolution solution) { } } "; - var expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 27).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 27).WithArguments("IVsSolution", "Test.VerifyOnUIThread"); await new Verify.Test { TestCode = test, @@ -1814,7 +1817,8 @@ class Test } } "; - var expected = new DiagnosticResult[] { + var expected = new DiagnosticResult[] + { Verify.Diagnostic(DescriptorSync).WithSpan(11, 9, 11, 12).WithArguments("Test.Foo", "Test.VerifyOnUIThread"), Verify.Diagnostic(DescriptorAsync).WithSpan(21, 9, 21, 14).WithArguments("Test.Reset", "JoinableTaskFactory.SwitchToMainThreadAsync"), }; @@ -1853,7 +1857,8 @@ class Test } } "; - var expect = new DiagnosticResult[] { + var expect = new DiagnosticResult[] + { Verify.Diagnostic(DescriptorSync).WithSpan(11, 9, 11, 12).WithArguments("Test.Foo", "Test.VerifyOnUIThread"), }; await Verify.VerifyAnalyzerAsync(test, expect); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs similarity index 78% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs index ddf37c53..29924acc 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD011UseAsyncLazyAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -21,7 +24,7 @@ class Test { Lazy tInt = new Lazy(); } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -36,7 +39,7 @@ class Test { Lazy> t3 = new {|#0:Lazy>|}(); } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -51,7 +54,7 @@ class Test { Lazy t3 = new {|#0:Lazy|}(); } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -77,7 +80,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.SyncBlockInValueFactoryDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.SyncBlockInValueFactoryDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -103,7 +106,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.SyncBlockInValueFactoryDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.SyncBlockInValueFactoryDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -129,7 +132,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -175,7 +178,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); + DiagnosticResult expected = this.CreateDiagnostic(AbstractVSTHRD011UseAsyncLazyAnalyzer.LazyOfTaskDescriptor, 0); await Verify.VerifyAnalyzerAsync(test, expected); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs index f4cf6db5..0a6e9ed3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD012SpecifyJtfWhereAllowedTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -24,7 +27,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, @@ -50,7 +53,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, @@ -120,7 +123,7 @@ class Apple { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, @@ -166,7 +169,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs similarity index 73% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs index 16a7c53b..0eeeeca8 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -17,7 +20,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -32,7 +35,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -47,7 +50,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -70,7 +73,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -97,7 +100,7 @@ class Test { class MyEventArgs : EventArgs {} "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyCodeFixAsync(test, expected, withFix); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs similarity index 73% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs index 8d7b35c3..7e3ea752 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD100AsyncVoidMethodCodeFixTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -27,7 +30,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(5, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(5, 16); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -54,8 +57,8 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(6, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(6, 16); await Verify.VerifyCodeFixAsync(test, expected, withFix); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs similarity index 80% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs index d355bd93..f125f63b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD101AsyncVoidLambdaAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -23,7 +26,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -43,7 +46,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -63,7 +66,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -83,7 +86,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -99,7 +102,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -115,7 +118,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs index be33c489..534b0dd3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD102AvoidJtfRunInNonPublicMembersAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -20,7 +23,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(8, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(8, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -74,7 +77,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(8, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(8, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -109,7 +112,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(8, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(8, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -127,7 +130,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(8, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(8, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -171,7 +174,7 @@ class Test : IFoo { } } "; - var expected = Verify.Diagnostic().WithLocation(13, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(13, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -194,7 +197,7 @@ class Test : IFoo { } } "; - var expected = Verify.Diagnostic().WithLocation(13, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(13, 13); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -256,7 +259,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(8, 13); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(8, 13); await Verify.VerifyAnalyzerAsync(test, expected); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs similarity index 86% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs index 4482a7d1..c9312fda 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -41,7 +44,7 @@ class Test { void Run() { } } "; - var expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -86,7 +89,7 @@ class Test { void Run() { } } "; - var expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -122,7 +125,7 @@ class Test { void Run() { } } "; - var expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithLocation(8, 13).WithArguments("Run", "RunAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -160,7 +163,7 @@ class Test { } "; - var expected = Verify.Diagnostic(Descriptor).WithLocation(8, 26).WithArguments("Run", "RunAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithLocation(8, 26).WithArguments("Run", "RunAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -200,7 +203,7 @@ class Test { } "; - var expected = Verify.Diagnostic(Descriptor).WithLocation(9, 12).WithArguments("Join", "JoinAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithLocation(9, 12).WithArguments("Join", "JoinAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -229,7 +232,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 11).WithArguments("Wait"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 11).WithArguments("Wait"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -264,7 +267,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(10, 11, 4, "Wait"); + DiagnosticResult expected = this.CreateDiagnostic(10, 11, 4, "Wait"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -299,7 +302,7 @@ class Test { } } "; - var expected = this.CreateDiagnostic(10, 27, 9, "GetResult"); + DiagnosticResult expected = this.CreateDiagnostic(10, 27, 9, "GetResult"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -323,20 +326,20 @@ class Test { "; string withFix = test; -//// var withFix = @" -//// using System.Threading.Tasks; -//// using Microsoft.VisualStudio.Shell; -//// using Microsoft.VisualStudio.Shell.Interop; -//// using Task = System.Threading.Tasks.Task; -//// -//// class Test { -//// async Task T() { -//// IVsTask t = null; -//// object result = await t; -//// } -//// } -//// "; - var expected = this.CreateDiagnostic(8, 27, 9, "GetResult"); + //// var withFix = @" + //// using System.Threading.Tasks; + //// using Microsoft.VisualStudio.Shell; + //// using Microsoft.VisualStudio.Shell.Interop; + //// using Task = System.Threading.Tasks.Task; + //// + //// class Test { + //// async Task T() { + //// IVsTask t = null; + //// object result = await t; + //// } + //// } + //// "; + DiagnosticResult expected = this.CreateDiagnostic(8, 27, 9, "GetResult"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -367,7 +370,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 24).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 24).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -405,7 +408,7 @@ static class Assert { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 26).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 26).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -434,7 +437,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 45).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 45).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -471,7 +474,7 @@ class Test { } "; - var expected = this.CreateDiagnostic(9, 28, 6, "Result"); + DiagnosticResult expected = this.CreateDiagnostic(9, 28, 6, "Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -508,7 +511,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(9, 28, 9, 34).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(9, 28, 9, 34).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -539,7 +542,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(8, 57, 8, 63).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(8, 57, 8, 63).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -576,7 +579,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(9, 28, 9, 34).WithArguments("Result"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(9, 28, 9, 34).WithArguments("Result"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -625,7 +628,7 @@ class Test { } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 24).WithArguments("GetResult"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 24).WithArguments("GetResult"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -659,7 +662,7 @@ class Test { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(6, 9, 6, 12).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(6, 9, 6, 12).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -720,7 +723,7 @@ class Test { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(6, 9, 6, 12).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(6, 9, 6, 12).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -754,7 +757,7 @@ class Test { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(6, 17, 6, 20).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(6, 17, 6, 20).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -792,7 +795,7 @@ class Util { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(6, 14, 6, 17).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(6, 14, 6, 17).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -860,7 +863,7 @@ class Apple : Fruit { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(7, 11, 7, 14).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(7, 11, 7, 14).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -906,7 +909,7 @@ static class FruitUtils { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(7, 11, 7, 14).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(7, 11, 7, 14).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -946,7 +949,7 @@ static class FruitUtils { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(7, 9, 7, 12).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(7, 9, 7, 12).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -1057,7 +1060,7 @@ static class FruitUtils { } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(7, 9, 7, 17).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(7, 9, 7, 17).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -1100,7 +1103,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(Descriptor).WithSpan(15, 9, 15, 12).WithArguments("Foo", "FooAsync"); + DiagnosticResult expected = Verify.Diagnostic(Descriptor).WithSpan(15, 9, 15, 12).WithArguments("Foo", "FooAsync"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -1143,7 +1146,7 @@ class Test { } } "; - var expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(15, 34, 15, 38).WithArguments("Wait"); + DiagnosticResult expected = Verify.Diagnostic(DescriptorNoAlternativeMethod).WithSpan(15, 34, 15, 38).WithArguments("Wait"); await Verify.VerifyCodeFixAsync(test, expected, withFix); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs similarity index 83% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs index f8bf113e..ec1440d0 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD104OfferAsyncOptionAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -24,7 +27,7 @@ public class Test { } "; - var expected = Verify.Diagnostic().WithSpan(9, 13, 9, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 13, 9, 16); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -96,7 +99,7 @@ public class Test { } "; - var expected = Verify.Diagnostic().WithSpan(9, 13, 9, 16); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 13, 9, 16); await Verify.VerifyAnalyzerAsync(test, expected); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs similarity index 87% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs index 58bb84db..fc9e2fa6 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD105AvoidImplicitTaskSchedulerCurrentAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -21,7 +24,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, @@ -43,7 +46,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithLocation(0); + DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await new Verify.Test { TestCode = test, diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs similarity index 97% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs index 538522bf..60cf580d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD106UseInvokeAsyncForAsyncEventsAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs similarity index 83% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs index bf86fa93..7d801e82 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD107AwaitTaskWithinUsingExpressionAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -36,7 +39,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -74,7 +77,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -110,7 +113,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -144,7 +147,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(8, 16, 8, 32); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -180,7 +183,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(9, 16, 9, 24); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 16, 9, 24); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -222,7 +225,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(9, 16, 9, 27); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 16, 9, 27); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -244,7 +247,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(9, 16, 9, 19); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithSpan(9, 16, 9, 19); await Verify.VerifyAnalyzerAsync(test, expected); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs similarity index 84% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs index 96c35db1..b87af79f 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD108AssertThreadRequirementUnconditionallyTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; @@ -40,7 +43,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -86,7 +89,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -108,7 +111,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -130,7 +133,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -168,7 +171,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -192,7 +195,7 @@ class Test { } } "; - var expected = Verify.Diagnostic().WithLocation(0); + CodeAnalysis.Testing.DiagnosticResult expected = Verify.Diagnostic().WithLocation(0); await Verify.VerifyAnalyzerAsync(test, expected); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs similarity index 97% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs index e000ba00..2df7509b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD109AvoidAssertInAsyncMethodsAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs similarity index 87% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs index 9fcd8aaa..bceb8a25 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD110ObserveResultOfAsyncCallsAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -23,7 +26,7 @@ class Test { } "; - var expected = this.CreateDiagnostic(7, 9, 8); + DiagnosticResult expected = this.CreateDiagnostic(7, 9, 8); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -45,7 +48,7 @@ class Test { } "; - var expected = this.CreateDiagnostic(8, 13, 8); + DiagnosticResult expected = this.CreateDiagnostic(8, 13, 8); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -169,7 +172,7 @@ class Test { } "; - var expected = this.CreateDiagnostic(7, 20, 12); + DiagnosticResult expected = this.CreateDiagnostic(7, 20, 12); await Verify.VerifyAnalyzerAsync(test, expected); } @@ -207,7 +210,7 @@ class Test { } "; - var expected = DiagnosticResult.CompilerError("CS0103").WithLocation(6, 9).WithArguments("a"); + DiagnosticResult expected = DiagnosticResult.CompilerError("CS0103").WithLocation(6, 9).WithArguments("a"); await Verify.VerifyAnalyzerAsync(test, expected); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs similarity index 94% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs index a1f5a884..166844e9 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs similarity index 98% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs index 271bcf00..8bbdb3b6 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD112ImplementSystemIAsyncDisposableAnalyzerTests.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs similarity index 98% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs index 124a95c4..2548b519 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD113CheckForSystemIAsyncDisposableAnalyzerTests.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs similarity index 96% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs index da812189..3955378b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs similarity index 96% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs index 8e5a580f..3f566819 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskCodeFixTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs similarity index 88% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs index 51a0de12..3798d09f 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -31,7 +34,7 @@ class Test { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -120,7 +123,7 @@ static class AwaitExtensions { } "; - var expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(4, 12, 4, 19); + DiagnosticResult expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(4, 12, 4, 19); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -143,7 +146,7 @@ class Test { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 15, 5, 18); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 15, 5, 18); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -166,7 +169,7 @@ class Test { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 20, 5, 23); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 20, 5, 23); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -189,7 +192,7 @@ class Test { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 27, 5, 30); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 27, 5, 30); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -284,7 +287,7 @@ class Test { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -372,7 +375,7 @@ class Test { } "; - var expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); + DiagnosticResult expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -395,7 +398,7 @@ class Test { } "; - var expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); + DiagnosticResult expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -426,7 +429,7 @@ class Test : IFoo { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -457,7 +460,7 @@ class Test : IFoo { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -488,7 +491,7 @@ class Test : MyBase { } "; - var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 26, 5, 29); + DiagnosticResult expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 26, 5, 29); await Verify.VerifyCodeFixAsync(test, expected, withFix); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/app.config b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/app.config similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Analyzers.Tests/app.config rename to test/Microsoft.VisualStudio.Threading.Analyzers.Tests/app.config diff --git a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/App.config b/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/App.config similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/App.config rename to test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/App.config diff --git a/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj b/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj new file mode 100644 index 00000000..4d83eb0c --- /dev/null +++ b/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj @@ -0,0 +1,10 @@ + + + net472 + Exe + + + + + + diff --git a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs b/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs similarity index 54% rename from src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs rename to test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs index 64821073..d950e89f 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher/Program.cs @@ -1,15 +1,18 @@ -namespace Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher { using Microsoft.Win32; internal static class Program { - private static void Main(string[] args) + private static void Main() { // Watch a registry key. Then try to exit the program. // If in Win7 support mode we start a thread to monitor for registry changes, // this verifies that the thread is a *background* thread that won't keep the process running. - var key = Registry.CurrentUser.OpenSubKey("SOFTWARE"); + RegistryKey? key = Registry.CurrentUser.OpenSubKey("SOFTWARE"); key.WaitForChangeAsync(); } } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/.gitignore b/test/Microsoft.VisualStudio.Threading.Tests/.gitignore similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Tests/.gitignore rename to test/Microsoft.VisualStudio.Threading.Tests/.gitignore diff --git a/src/Microsoft.VisualStudio.Threading.Tests/App.config b/test/Microsoft.VisualStudio.Threading.Tests/App.config similarity index 100% rename from src/Microsoft.VisualStudio.Threading.Tests/App.config rename to test/Microsoft.VisualStudio.Threading.Tests/App.config diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs b/test/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs similarity index 54% rename from src/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs index 75dfa7ca..1e1b0b02 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AssemblyInfo.cs @@ -1,4 +1,7 @@ -using Xunit; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; // Some of our tests measure stress, GC pressure, etc. // It messes with reliable test results when other threads are doing random stuff. diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs b/test/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs similarity index 88% rename from src/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs index d26691be..bb96cc6c 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AssertEx.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs similarity index 82% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs index 4ba504a6..96233bff 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -29,7 +32,7 @@ { for (int i = 0; i < 5; i++) { - var t = this.evt.WaitAsync(); + Task? t = this.evt.WaitAsync(); Assert.False(t.IsCompleted); this.evt.Set(); await t; @@ -42,7 +45,7 @@ this.evt.Set(); this.evt.Set(); await this.evt.WaitAsync(); - var t = this.evt.WaitAsync(); + Task? t = this.evt.WaitAsync(); Assert.False(t.IsCompleted); await Task.Delay(AsyncDelay); Assert.False(t.IsCompleted); @@ -71,14 +74,15 @@ public void SetReturnsBeforeInlinedContinuations() { var setReturned = new ManualResetEventSlim(); - var inlinedContinuation = this.evt.WaitAsync() - .ContinueWith(delegate - { - // Arrange to synchronously block the continuation until Set() has returned, - // which would deadlock if Set does not return until inlined continuations complete. - Assert.True(setReturned.Wait(AsyncDelay)); - }, - TaskContinuationOptions.ExecuteSynchronously); + Task? inlinedContinuation = this.evt.WaitAsync() + .ContinueWith( + delegate + { + // Arrange to synchronously block the continuation until Set() has returned, + // which would deadlock if Set does not return until inlined continuations complete. + Assert.True(setReturned.Wait(AsyncDelay)); + }, + TaskContinuationOptions.ExecuteSynchronously); this.evt.Set(); setReturned.Set(); Assert.True(inlinedContinuation.Wait(UnexpectedTimeout)); @@ -93,15 +97,16 @@ this.evt = new AsyncAutoResetEvent(allowInliningAwaiters: true); Thread settingThread = Thread.CurrentThread; bool setReturned = false; - var inlinedContinuation = this.evt.WaitAsync() - .ContinueWith(delegate - { - // Arrange to synchronously block the continuation until Set() has returned, - // which would deadlock if Set does not return until inlined continuations complete. - Assert.False(setReturned); - Assert.Same(settingThread, Thread.CurrentThread); - }, - TaskContinuationOptions.ExecuteSynchronously); + Task? inlinedContinuation = this.evt.WaitAsync() + .ContinueWith( + delegate + { + // Arrange to synchronously block the continuation until Set() has returned, + // which would deadlock if Set does not return until inlined continuations complete. + Assert.False(setReturned); + Assert.Same(settingThread, Thread.CurrentThread); + }, + TaskContinuationOptions.ExecuteSynchronously); this.evt.Set(); setReturned = true; Assert.True(inlinedContinuation.IsCompleted); @@ -140,7 +145,7 @@ // a meaningful identity check later. var tokenSource = new CancellationTokenSource(); tokenSource.Cancel(); - var token = tokenSource.Token; + CancellationToken token = tokenSource.Token; // Verify that a pre-set signal is not reset by a canceled wait request. this.evt.Set(); @@ -163,7 +168,7 @@ public void WaitAsync_Canceled_DoesNotInlineContinuations() { var cts = new CancellationTokenSource(); - var task = this.evt.WaitAsync(cts.Token); + Task? task = this.evt.WaitAsync(cts.Token); VerifyDoesNotInlineContinuations(task, () => cts.Cancel()); } @@ -172,7 +177,7 @@ { this.evt = new AsyncAutoResetEvent(allowInliningAwaiters: true); var cts = new CancellationTokenSource(); - var task = this.evt.WaitAsync(cts.Token); + Task? task = this.evt.WaitAsync(cts.Token); VerifyCanInlineContinuations(task, () => cts.Cancel()); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs index 545f24c7..afee050c 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncBarrierTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Linq; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs index f48890b2..5eadf934 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncCountdownEventTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -59,10 +62,10 @@ { var evt = new AsyncCountdownEvent(2); - var first = evt.SignalAndWaitAsync(); + Task? first = evt.SignalAndWaitAsync(); Assert.False(first.IsCompleted); - var second = evt.SignalAndWaitAsync(); + Task? second = evt.SignalAndWaitAsync(); await Task.WhenAll(first, second); } @@ -82,7 +85,7 @@ { var evt = new AsyncCountdownEvent(0); #pragma warning disable CS0618 // Type or member is obsolete - var result = evt.SignalAsync(); + Task? result = evt.SignalAsync(); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(result.IsFaulted); Assert.IsType(result.Exception!.InnerException); @@ -95,7 +98,7 @@ public void SignalAndWaitAsyncReturnsFaultedTaskOnError() { var evt = new AsyncCountdownEvent(0); - var result = evt.SignalAndWaitAsync(); + Task? result = evt.SignalAndWaitAsync(); Assert.True(result.IsFaulted); Assert.IsType(result.Exception!.InnerException); } @@ -124,7 +127,7 @@ private async Task PostSignalHelperAsync(int initialCount) { var evt = new AsyncCountdownEvent(initialCount); - var waiter = evt.WaitAsync(); + Task? waiter = evt.WaitAsync(); for (int i = 0; i < initialCount; i++) { diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs index f51fba7e..757db168 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyInitializerTests.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { @@ -25,7 +22,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public void Ctor_NullAction(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. Assert.Throws(() => new AsyncLazyInitializer(null!, jtf)); } @@ -140,8 +137,8 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void InitializeAsync_RequiresMainThreadHeldByOther() { - var context = this.InitializeJTCAndSC(); - var jtf = context.Factory; + JoinableTaskContext? context = this.InitializeJTCAndSC(); + JoinableTaskFactory? jtf = context.Factory; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazyInitializer( @@ -151,11 +148,11 @@ namespace Microsoft.VisualStudio.Threading.Tests }, jtf); - var resultTask = lazy.InitializeAsync(); + Task? resultTask = lazy.InitializeAsync(); Assert.False(resultTask.IsCompleted); - var collection = context.CreateCollection(); - var someRandomPump = context.CreateFactory(collection); + JoinableTaskCollection? collection = context.CreateCollection(); + JoinableTaskFactory? someRandomPump = context.CreateFactory(collection); someRandomPump.Run(async delegate { evt.Set(); // setting this event allows the value factory to resume, once it can get the Main thread. @@ -183,8 +180,8 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void Initialize_RequiresMainThreadHeldByOther() { - var context = this.InitializeJTCAndSC(); - var jtf = context.Factory; + JoinableTaskContext? context = this.InitializeJTCAndSC(); + JoinableTaskFactory? jtf = context.Factory; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazyInitializer( @@ -194,7 +191,7 @@ namespace Microsoft.VisualStudio.Threading.Tests }, jtf); - var resultTask = lazy.InitializeAsync(); + Task? resultTask = lazy.InitializeAsync(); Assert.False(resultTask.IsCompleted); evt.Set(); // setting this event allows the value factory to resume, once it can get the Main thread. diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs similarity index 81% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs index e9ef85cd..d539b669 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLazyTests.cs @@ -1,13 +1,11 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; + using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -15,7 +13,7 @@ namespace Microsoft.VisualStudio.Threading.Tests using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; - using NamedSyncContexts = Microsoft.VisualStudio.Threading.Tests.AwaitExtensionsTests.NamedSyncContexts; + using NamedSyncContext = Microsoft.VisualStudio.Threading.Tests.AwaitExtensionsTests.NamedSyncContext; public class AsyncLazyTests : TestBase { @@ -34,7 +32,7 @@ namespace Microsoft.VisualStudio.Threading.Tests return expected; }); - var actual = await lazy.GetValueAsync(); + GenericParameterHelper? actual = await lazy.GetValueAsync(); Assert.Same(expected, actual); } @@ -51,7 +49,7 @@ namespace Microsoft.VisualStudio.Threading.Tests }); Assert.False(lazy.IsValueCreated); - var resultTask = lazy.GetValueAsync(); + Task? resultTask = lazy.GetValueAsync(); Assert.True(lazy.IsValueCreated); evt.Set(); Assert.Equal(5, (await resultTask).Data); @@ -69,7 +67,7 @@ namespace Microsoft.VisualStudio.Threading.Tests }); Assert.False(lazy.IsValueFactoryCompleted); - var resultTask = lazy.GetValueAsync(); + Task? resultTask = lazy.GetValueAsync(); Assert.False(lazy.IsValueFactoryCompleted); evt.Set(); Assert.Equal(5, (await resultTask).Data); @@ -89,7 +87,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public async Task ValueFactoryExecutedOnlyOnceSequential(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. bool valueFactoryExecuted = false; var lazy = new AsyncLazy( async delegate @@ -101,10 +99,10 @@ namespace Microsoft.VisualStudio.Threading.Tests }, jtf); - var task1 = lazy.GetValueAsync(); - var task2 = lazy.GetValueAsync(); - var actual1 = await task1; - var actual2 = await task2; + Task? task1 = lazy.GetValueAsync(); + Task? task2 = lazy.GetValueAsync(); + GenericParameterHelper? actual1 = await task1; + GenericParameterHelper? actual2 = await task2; Assert.Same(actual1, actual2); Assert.Equal(5, actual1.Data); } @@ -116,7 +114,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public void ValueFactoryExecutedOnlyOnceConcurrent(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var cts = new CancellationTokenSource(AsyncDelay); while (!cts.Token.IsCancellationRequested) { @@ -135,7 +133,7 @@ namespace Microsoft.VisualStudio.Threading.Tests }, jtf); - var results = TestUtilities.ConcurrencyTest(delegate + GenericParameterHelper[]? results = TestUtilities.ConcurrencyTest(delegate { return lazy.GetValueAsync().Result; }); @@ -179,7 +177,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public void ValueFactoryThrowsSynchronously(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. bool executed = false; var lazy = new AsyncLazy( new Func>(delegate @@ -190,8 +188,8 @@ namespace Microsoft.VisualStudio.Threading.Tests }), jtf); - var task1 = lazy.GetValueAsync(); - var task2 = lazy.GetValueAsync(); + Task? task1 = lazy.GetValueAsync(); + Task? task2 = lazy.GetValueAsync(); Assert.Same(task1, task2); Assert.True(task1.IsFaulted); Assert.IsType(task1.Exception!.InnerException); @@ -200,7 +198,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public async Task ValueFactoryReentersValueFactorySynchronously(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. AsyncLazy? lazy = null; bool executed = false; lazy = new AsyncLazy( @@ -222,7 +220,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public async Task ValueFactoryReentersValueFactoryAsynchronously(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. AsyncLazy? lazy = null; bool executed = false; lazy = new AsyncLazy( @@ -253,8 +251,8 @@ namespace Microsoft.VisualStudio.Threading.Tests }); var cts = new CancellationTokenSource(); - var task1 = lazy.GetValueAsync(cts.Token); - var task2 = lazy.GetValueAsync(); + Task? task1 = lazy.GetValueAsync(cts.Token); + Task? task2 = lazy.GetValueAsync(); cts.Cancel(); // Verify that the task returned from the canceled request actually completes before the value factory does. @@ -262,7 +260,7 @@ namespace Microsoft.VisualStudio.Threading.Tests // Now verify that the value factory does actually complete anyway for other callers. evt.Set(); - var task2Result = await task2; + GenericParameterHelper? task2Result = await task2; Assert.Equal(5, task2Result.Data); } @@ -285,7 +283,7 @@ namespace Microsoft.VisualStudio.Threading.Tests var cts = new CancellationTokenSource(); cts.Cancel(); - var result = await lazy.GetValueAsync(cts.Token); // this shouldn't throw canceled because it was already done. + GenericParameterHelper? result = await lazy.GetValueAsync(cts.Token); // this shouldn't throw canceled because it was already done. Assert.Equal(5, result.Data); Assert.True(lazy.IsValueCreated); Assert.True(lazy.IsValueFactoryCompleted); @@ -294,7 +292,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public void GetValue(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy(() => Task.FromResult(new GenericParameterHelper(5)), jtf); Assert.Equal(5, lazy.GetValue().Data); } @@ -302,7 +300,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public void GetValue_Precanceled(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy(() => Task.FromResult(new GenericParameterHelper(5)), jtf); Assert.Throws(() => lazy.GetValue(new CancellationToken(canceled: true))); } @@ -310,9 +308,9 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory, CombinatorialData] public async Task GetValue_CalledAfterGetValueAsyncHasCompleted(bool specifyJtf) { - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy(() => Task.FromResult(new GenericParameterHelper(5)), jtf); - var result = await lazy.GetValueAsync(); + GenericParameterHelper? result = await lazy.GetValueAsync(); Assert.Same(result, lazy.GetValue()); } @@ -320,7 +318,7 @@ namespace Microsoft.VisualStudio.Threading.Tests public async Task GetValue_CalledAfterGetValueAsync_InProgress(bool specifyJtf) { var completeValueFactory = new AsyncManualResetEvent(); - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy( async delegate { @@ -341,7 +339,7 @@ namespace Microsoft.VisualStudio.Threading.Tests public async Task GetValue_ThenCanceled(bool specifyJtf) { var completeValueFactory = new AsyncManualResetEvent(); - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy( async delegate { @@ -361,7 +359,7 @@ namespace Microsoft.VisualStudio.Threading.Tests { var exception = new InvalidOperationException(); var completeValueFactory = new AsyncManualResetEvent(); - var jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. + JoinableTaskFactory? jtf = specifyJtf ? new JoinableTaskContext().Factory : null; // use our own so we don't get main thread deadlocks, which isn't the point of this test. var lazy = new AsyncLazy(() => throw exception, jtf); // Verify that we throw the right exception the first time. @@ -387,7 +385,7 @@ namespace Microsoft.VisualStudio.Threading.Tests var lazy = new AsyncLazy(() => Task.FromResult(3)); var value = await lazy.GetValueAsync(); string result = lazy.ToString(); - Assert.Equal(value.ToString(), result); + Assert.Equal(value.ToString(CultureInfo.InvariantCulture), result); } [Fact] @@ -406,31 +404,31 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory] [CombinatorialData] - public void AsyncLazy_CompletesOnThreadWithValueFactory(NamedSyncContexts invokeOn, NamedSyncContexts completeOn) + public void AsyncLazy_CompletesOnThreadWithValueFactory(NamedSyncContext invokeOn, NamedSyncContext completeOn) { // Set up various SynchronizationContexts that we may invoke or complete the async method with. - var aSyncContext = SingleThreadedTestSynchronizationContext.New(); - var bSyncContext = SingleThreadedTestSynchronizationContext.New(); - var invokeOnSyncContext = invokeOn == NamedSyncContexts.None ? null - : invokeOn == NamedSyncContexts.A ? aSyncContext - : invokeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? aSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? bSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? invokeOnSyncContext = invokeOn == NamedSyncContext.None ? null + : invokeOn == NamedSyncContext.A ? aSyncContext + : invokeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(invokeOn)); - var completeOnSyncContext = completeOn == NamedSyncContexts.None ? null - : completeOn == NamedSyncContexts.A ? aSyncContext - : completeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? completeOnSyncContext = completeOn == NamedSyncContext.None ? null + : completeOn == NamedSyncContext.A ? aSyncContext + : completeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(completeOn)); // Set up a single-threaded SynchronizationContext that we'll invoke the async method within. SynchronizationContext.SetSynchronizationContext(invokeOnSyncContext); var unblockAsyncMethod = new TaskCompletionSource(); - var getValueTask = new AsyncLazy(async delegate + Task? getValueTask = new AsyncLazy(async delegate { await unblockAsyncMethod.Task.ConfigureAwaitRunInline(); return Environment.CurrentManagedThreadId; }).GetValueAsync(); - var verificationTask = getValueTask.ContinueWith( + Task? verificationTask = getValueTask.ContinueWith( lazyValue => Environment.CurrentManagedThreadId, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, @@ -453,8 +451,8 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void ValueFactoryRequiresMainThreadHeldByOther() { - var context = this.InitializeJTCAndSC(); - var jtf = context.Factory; + JoinableTaskContext? context = this.InitializeJTCAndSC(); + JoinableTaskFactory? jtf = context.Factory; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy( @@ -465,11 +463,11 @@ namespace Microsoft.VisualStudio.Threading.Tests }, jtf); - var resultTask = lazy.GetValueAsync(); + Task? resultTask = lazy.GetValueAsync(); Assert.False(resultTask.IsCompleted); - var collection = context.CreateCollection(); - var someRandomPump = context.CreateFactory(collection); + JoinableTaskCollection? collection = context.CreateCollection(); + JoinableTaskFactory? someRandomPump = context.CreateFactory(collection); someRandomPump.Run(async delegate { evt.Set(); // setting this event allows the value factory to resume, once it can get the Main thread. @@ -496,11 +494,11 @@ namespace Microsoft.VisualStudio.Threading.Tests [CombinatorialData] public void ValueFactoryRequiresMainThreadHeldByOtherSync(bool passJtfToLazyCtor) { - var ctxt = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? ctxt = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(ctxt); var context = new JoinableTaskContext(); - var asyncPump = context.Factory; - var originalThread = Thread.CurrentThread; + JoinableTaskFactory? asyncPump = context.Factory; + Thread? originalThread = Thread.CurrentThread; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy( @@ -524,9 +522,9 @@ namespace Microsoft.VisualStudio.Threading.Tests }); Thread.Sleep(AsyncDelay); // Give the background thread time to call GetValueAsync(), but it doesn't yield (when the test was written). - var foregroundRequest = lazy.GetValueAsync(); + Task? foregroundRequest = lazy.GetValueAsync(); - var frame = SingleThreadedTestSynchronizationContext.NewFrame(); + SingleThreadedTestSynchronizationContext.IFrame? frame = SingleThreadedTestSynchronizationContext.NewFrame(); var combinedTask = Task.WhenAll(foregroundRequest, backgroundRequest); combinedTask.WithTimeout(UnexpectedTimeout).ContinueWith(_ => frame.Continue = false, TaskScheduler.Default); SingleThreadedTestSynchronizationContext.PushFrame(ctxt, frame); @@ -544,11 +542,11 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void ValueFactoryRequiresMainThreadHeldByOtherInJTFRun() { - var ctxt = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? ctxt = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(ctxt); var context = new JoinableTaskContext(); - var asyncPump = context.Factory; - var originalThread = Thread.CurrentThread; + JoinableTaskFactory? asyncPump = context.Factory; + Thread? originalThread = Thread.CurrentThread; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy( @@ -587,11 +585,11 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void ValueFactoryRequiresMainThreadHeldByOtherInGetValue() { - var ctxt = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? ctxt = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(ctxt); var context = new JoinableTaskContext(); - var asyncPump = context.Factory; - var originalThread = Thread.CurrentThread; + JoinableTaskFactory? asyncPump = context.Factory; + Thread? originalThread = Thread.CurrentThread; var evt = new AsyncManualResetEvent(); var lazy = new AsyncLazy( @@ -636,8 +634,8 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public async Task ExecutionContextFlowsFromFirstCaller_JTF() { - var context = this.InitializeJTCAndSC(); - var jtf = context.Factory; + JoinableTaskContext? context = this.InitializeJTCAndSC(); + JoinableTaskFactory? jtf = context.Factory; var asyncLocal = new Threading.AsyncLocal(); var asyncLazy = new AsyncLazy( delegate @@ -670,7 +668,7 @@ namespace Microsoft.VisualStudio.Threading.Tests var writeLockTask = Task.Run(async delegate { await readLockAcquiredByOther; - var writeAwaiter = lck.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = lck.WriteLockAsync().GetAwaiter(); writeAwaiter.OnCompleted(delegate { using (writeAwaiter.GetResult()) @@ -681,7 +679,7 @@ namespace Microsoft.VisualStudio.Threading.Tests }); // Kick off the value factory without any lock context. - var resultTask = lazy.GetValueAsync(); + Task? resultTask = lazy.GetValueAsync(); using (await lck.ReadLockAsync()) { @@ -721,7 +719,7 @@ namespace Microsoft.VisualStudio.Threading.Tests { var context = new JoinableTaskContext(); // we need our own collectible context. var collectible = new WeakReference(context.Factory); - var valueFactory = throwInValueFactory + Func>? valueFactory = throwInValueFactory ? new Func>(() => throw new ApplicationException()) : async delegate { diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs index adb9e7ce..1ef06790 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncLocalTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -159,13 +162,14 @@ [Fact, Trait("Performance", "true")] public void AsyncLocalPerfTest() { - var values = Enumerable.Range(1, 50000).Select(n => new GenericParameterHelper(n)).ToArray(); + GenericParameterHelper[]? values = Enumerable.Range(1, 50000).Select(n => new GenericParameterHelper(n)).ToArray(); var creates = Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { this.asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal(); } + creates.Stop(); var writes = Stopwatch.StartNew(); @@ -179,7 +183,7 @@ var reads = Stopwatch.StartNew(); for (int i = 0; i < values.Length; i++) { - var value = this.asyncLocal.Value; + GenericParameterHelper? value = this.asyncLocal.Value; } reads.Stop(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs similarity index 84% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs index 98b461cd..4b57624b 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncManualResetEventTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -48,14 +51,15 @@ public void SetReturnsBeforeInlinedContinuations() { var setReturned = new ManualResetEventSlim(); - var inlinedContinuation = this.evt.WaitAsync() - .ContinueWith(delegate - { - // Arrange to synchronously block the continuation until Set() has returned, - // which would deadlock if Set does not return until inlined continuations complete. - Assert.True(setReturned.Wait(AsyncDelay)); - }, - TaskContinuationOptions.ExecuteSynchronously); + Task? inlinedContinuation = this.evt.WaitAsync() + .ContinueWith( + delegate + { + // Arrange to synchronously block the continuation until Set() has returned, + // which would deadlock if Set does not return until inlined continuations complete. + Assert.True(setReturned.Wait(AsyncDelay)); + }, + TaskContinuationOptions.ExecuteSynchronously); this.evt.Set(); Assert.True(this.evt.IsSet); setReturned.Set(); @@ -66,7 +70,7 @@ public async Task Blocking() { this.evt.Reset(); - var result = this.evt.WaitAsync(); + Task? result = this.evt.WaitAsync(); Assert.False(result.IsCompleted); this.evt.Set(); await result; @@ -79,7 +83,7 @@ await this.evt.SetAsync(); #pragma warning restore CS0618 // Type or member is obsolete this.evt.Reset(); - var result = this.evt.WaitAsync(); + Task? result = this.evt.WaitAsync(); Assert.False(result.IsCompleted); } @@ -97,9 +101,9 @@ [Fact] public void PulseAllAsync() { - var waitTask = this.evt.WaitAsync(); + Task? waitTask = this.evt.WaitAsync(); #pragma warning disable CS0618 // Type or member is obsolete - var pulseTask = this.evt.PulseAllAsync(); + Task? pulseTask = this.evt.PulseAllAsync(); #pragma warning restore CS0618 // Type or member is obsolete Assert.Equal(TaskStatus.RanToCompletion, pulseTask.Status); Assert.True(waitTask.IsCompleted); @@ -109,7 +113,7 @@ [Fact] public void PulseAll() { - var task = this.evt.WaitAsync(); + Task? task = this.evt.WaitAsync(); this.evt.PulseAll(); Assert.True(task.IsCompleted); Assert.False(this.evt.WaitAsync().IsCompleted); @@ -145,11 +149,11 @@ // We starve the threadpool so that if SetAsync() // does work asynchronously, we'll force it to happen // after the Reset() method is executed. - using (var starvation = TestUtilities.StarveThreadpool()) + using (IDisposable? starvation = TestUtilities.StarveThreadpool()) { #pragma warning disable CS0618 // Type or member is obsolete // Set and immediately reset the event. - var setTask = this.evt.SetAsync(); + Task? setTask = this.evt.SetAsync(); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(this.evt.IsSet); this.evt.Reset(); @@ -216,7 +220,7 @@ var cts = new CancellationTokenSource(); Task waitTask = this.evt.WaitAsync(cts.Token); cts.Cancel(); - var ex = await Assert.ThrowsAnyAsync(() => waitTask); + OperationCanceledException? ex = await Assert.ThrowsAnyAsync(() => waitTask); Assert.Equal(cts.Token, ex.CancellationToken); } } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs index b731aac5..a4146811 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncQueueTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -153,7 +156,7 @@ { var enqueuedValue = new GenericParameterHelper(1); this.queue.Enqueue(enqueuedValue); - var peekedValue = this.queue.Peek(); + GenericParameterHelper? peekedValue = this.queue.Peek(); Assert.Same(enqueuedValue, peekedValue); // Peeking again should yield the same result. @@ -178,9 +181,9 @@ { var enqueuedValue = new GenericParameterHelper(1); this.queue.Enqueue(enqueuedValue); - var dequeueTask = this.queue.DequeueAsync(CancellationToken.None); + Task? dequeueTask = this.queue.DequeueAsync(CancellationToken.None); Assert.True(dequeueTask.GetAwaiter().IsCompleted); - var dequeuedValue = await dequeueTask; + GenericParameterHelper? dequeuedValue = await dequeueTask; Assert.Same(enqueuedValue, dequeuedValue); Assert.Equal(0, this.queue.Count); Assert.True(this.queue.IsEmpty); @@ -189,13 +192,13 @@ [Fact] public async Task DequeueAsyncNonBlockingWait() { - var dequeueTask = this.queue.DequeueAsync(CancellationToken.None); + Task? dequeueTask = this.queue.DequeueAsync(CancellationToken.None); Assert.False(dequeueTask.GetAwaiter().IsCompleted); var enqueuedValue = new GenericParameterHelper(1); this.queue.Enqueue(enqueuedValue); - var dequeuedValue = await dequeueTask; + GenericParameterHelper? dequeuedValue = await dequeueTask; Assert.Same(enqueuedValue, dequeuedValue); Assert.Equal(0, this.queue.Count); @@ -206,7 +209,7 @@ public async Task DequeueAsyncCancelledBeforeComplete() { var cts = new CancellationTokenSource(); - var dequeueTask = this.queue.DequeueAsync(cts.Token); + Task? dequeueTask = this.queue.DequeueAsync(cts.Token); Assert.False(dequeueTask.GetAwaiter().IsCompleted); cts.Cancel(); @@ -223,7 +226,7 @@ { var cts = new CancellationTokenSource(); cts.Cancel(); - var dequeueTask = this.queue.DequeueAsync(cts.Token); + Task? dequeueTask = this.queue.DequeueAsync(cts.Token); Assert.True(dequeueTask.GetAwaiter().IsCompleted); await Assert.ThrowsAsync(() => dequeueTask); @@ -237,7 +240,7 @@ public async Task DequeueAsyncCancelledAfterComplete() { var cts = new CancellationTokenSource(); - var dequeueTask = this.queue.DequeueAsync(cts.Token); + Task? dequeueTask = this.queue.DequeueAsync(cts.Token); Assert.False(dequeueTask.GetAwaiter().IsCompleted); var enqueuedValue = new GenericParameterHelper(1); @@ -245,7 +248,7 @@ cts.Cancel(); - var dequeuedValue = await dequeueTask; + GenericParameterHelper? dequeuedValue = await dequeueTask; Assert.True(this.queue.IsEmpty); Assert.Same(enqueuedValue, dequeuedValue); } @@ -269,7 +272,7 @@ { cts.Cancel(); }); - var dequeueTask = this.queue.DequeueAsync(cts.Token); + Task? dequeueTask = this.queue.DequeueAsync(cts.Token); await Assert.ThrowsAnyAsync(() => dequeueTask); } @@ -372,7 +375,7 @@ this.queue.Complete(); Assert.False(this.queue.Completion.IsCompleted); - var dequeuedValue = await this.queue.DequeueAsync(); + GenericParameterHelper? dequeuedValue = await this.queue.DequeueAsync(); Assert.Same(enqueuedValue, dequeuedValue); Assert.True(this.queue.Completion.IsCompleted); } @@ -393,7 +396,7 @@ [Fact] public async Task CompleteWhileDequeuersWaiting() { - var dequeueTask = this.queue.DequeueAsync(); + Task? dequeueTask = this.queue.DequeueAsync(); this.queue.Complete(); Assert.True(this.queue.Completion.IsCompleted); await Assert.ThrowsAnyAsync(() => dequeueTask).WithTimeout(UnexpectedTimeout); @@ -455,7 +458,7 @@ public void NoLockHeldForCancellationContinuation() { var cts = new CancellationTokenSource(); - var dequeueTask = this.queue.DequeueAsync(cts.Token); + Task? dequeueTask = this.queue.DequeueAsync(cts.Token); Task enqueueTask = dequeueTask.ContinueWith( delegate { @@ -504,7 +507,7 @@ callbackFired = true; }; - var dequeuer = queue.DequeueAsync(); + Task? dequeuer = queue.DequeueAsync(); queue.Enqueue(5); Assert.True(callbackFired); Assert.Equal(5, await dequeuer.WithTimeout(UnexpectedTimeout)); @@ -560,8 +563,8 @@ [Fact] public void DequeueAsyncContinuationsNotInlinedWithinPrivateLock() { - var dequeuerTask = this.queue.DequeueAsync(); - var continuationTask = dequeuerTask.ContinueWith( + Task? dequeuerTask = this.queue.DequeueAsync(); + Task? continuationTask = dequeuerTask.ContinueWith( _ => { Assert.True(Task.Run(delegate diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs similarity index 94% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs index 829ea4b3..6de4b9ae 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -50,6 +53,8 @@ { Assert.True(this.asyncLock.Completion.Wait(UnexpectedTimeout)); } + + this.asyncLock.Dispose(); } [Fact] @@ -67,12 +72,12 @@ await Task.Run(delegate { Assert.False(this.asyncLock.IsReadLockHeld); - var awaitable = this.asyncLock.ReadLockAsync(); + AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.ReadLockAsync(); Assert.False(this.asyncLock.IsReadLockHeld); - var awaiter = awaitable.GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter(); Assert.False(this.asyncLock.IsReadLockHeld); Assert.True(awaiter.IsCompleted); - var releaser = awaiter.GetResult(); + AsyncReaderWriterLock.Releaser releaser = awaiter.GetResult(); Assert.True(this.asyncLock.IsReadLockHeld); releaser.Dispose(); Assert.False(this.asyncLock.IsReadLockHeld); @@ -101,7 +106,7 @@ }); // Also verify that although the lock is hidden, a new lock may need to wait for this lock to finish. - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted, "The write lock should not be immediately available because a read lock is actually held."); writeAwaiter.OnCompleted(delegate { @@ -162,7 +167,7 @@ { var continuationFired = new TaskCompletionSource(); var releaseContinuation = new TaskCompletionSource(); - var continuation = this.asyncLock.Completion.ContinueWith( + Task? continuation = this.asyncLock.Completion.ContinueWith( delegate { continuationFired.SetAsync(); @@ -193,7 +198,7 @@ public async Task CompletionContinuationsExecuteAsynchronously() { var releaseContinuation = new TaskCompletionSource(); - var continuation = this.asyncLock.Completion.ContinueWith( + Task? continuation = this.asyncLock.Completion.ContinueWith( delegate { releaseContinuation.Task.Wait(); @@ -406,7 +411,7 @@ remoteTask = Task.Run(async delegate { Assert.True(this.asyncLock.IsReadLockHeld); - var nowait = firstLockObserved.SetAsync(); + Task? nowait = firstLockObserved.SetAsync(); await secondLockAcquired.Task; Assert.False(this.asyncLock.IsReadLockHeld, "Some remote call context saw a recycled lock issued to someone else."); }); @@ -439,7 +444,7 @@ remoteTask = Task.Run(async delegate { Assert.True(this.asyncLock.IsReadLockHeld); - var nowait = lockObservedOnce.SetAsync(); + Task? nowait = lockObservedOnce.SetAsync(); await nestedLockReleased.Task; Assert.True(this.asyncLock.IsReadLockHeld); nowait = lockObservedTwice.SetAsync(); @@ -451,13 +456,13 @@ await lockObservedOnce.Task; } - var nowait2 = nestedLockReleased.SetAsync(); + Task? nowait2 = nestedLockReleased.SetAsync(); await lockObservedTwice.Task; } using (await this.asyncLock.WriteLockAsync()) { - var nowait = secondLockAcquired.SetAsync(); + Task? nowait = secondLockAcquired.SetAsync(); await secondLockNotSeen.Task; } }); @@ -560,7 +565,7 @@ [Fact] public async Task IsAnyLockHeldReturnsFalseForIncompatibleSyncContexts() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); var asyncLock = new LockDerived(); using (await asyncLock.ReadLockAsync()) { @@ -573,7 +578,7 @@ [Fact] public async Task IsAnyPassiveLockHeldReturnsTrueForIncompatibleSyncContexts() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); var asyncLock = new LockDerived(); using (await asyncLock.ReadLockAsync()) { @@ -586,7 +591,7 @@ [Fact] public async Task IsPassiveReadLockHeldReturnsTrueForIncompatibleSyncContexts() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); using (await this.asyncLock.ReadLockAsync()) { Assert.True(this.asyncLock.IsPassiveReadLockHeld); @@ -598,7 +603,7 @@ [Fact] public async Task IsPassiveUpgradeableReadLockHeldReturnsTrueForIncompatibleSyncContexts() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); using (await this.asyncLock.UpgradeableReadLockAsync()) { Assert.True(this.asyncLock.IsPassiveUpgradeableReadLockHeld); @@ -610,7 +615,7 @@ [Fact] public async Task IsPassiveWriteLockHeldReturnsTrueForIncompatibleSyncContexts() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); await using (await this.asyncLock.WriteLockAsync()) { Assert.True(this.asyncLock.IsPassiveWriteLockHeld); @@ -619,8 +624,6 @@ } } -#region ReadLockAsync tests - [Fact] public async Task ReadLockAsyncSimple() { @@ -803,11 +806,11 @@ await Task.WhenAll( Task.Run(async delegate { - using (var outerReleaser = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser outerReleaser = await this.asyncLock.ReadLockAsync()) { await readLockHeld.SetAsync(); await writerQueued.Task; - using (var innerReleaser = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser innerReleaser = await this.asyncLock.ReadLockAsync()) { innerReleaser.Dispose(); // doing this here will lead to double-disposal at the close of the using block. } @@ -819,7 +822,7 @@ Task.Run(async delegate { await readLockHeld.Task; - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted); writeAwaiter.OnCompleted(delegate { @@ -865,14 +868,14 @@ { Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); // STA required. - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.Throws(() => awaiter.GetResult()); // throws on an STA thread } [StaFact] public void LockAsyncNotIssuedTillGetResultOnSta() { - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.False(this.asyncLock.IsReadLockHeld); try { @@ -891,7 +894,7 @@ { await Task.Run(delegate { - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); try { Assert.False(this.asyncLock.IsReadLockHeld); @@ -919,7 +922,7 @@ { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); try { Assert.False(awaiter.IsCompleted); @@ -987,7 +990,7 @@ using (await this.asyncLock.WriteLockAsync()) { Assert.True(this.asyncLock.IsWriteLockHeld); - var nowait = firstLockObtained.SetAsync(); + Task? nowait = firstLockObtained.SetAsync(); await Task.Delay(AsyncDelay); // hold it long enough to ensure our other thread blocks waiting for the read lock. Assert.True(this.asyncLock.IsWriteLockHeld); } @@ -1008,10 +1011,6 @@ })); } -#endregion - -#region UpgradeableReadLockAsync tests - [Fact] public async Task UpgradeableReadLockAsyncNoUpgrade() { @@ -1070,7 +1069,7 @@ Task.Run(async delegate { await firstUpgradeableReadHeld.Task; - var awaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted, "Second upgradeable read lock issued while first is still held."); awaiter.OnCompleted(delegate { @@ -1273,7 +1272,7 @@ var asyncLock = new LockDerived(); asyncLock.OnBeforeExclusiveLockReleasedAsyncDelegate = async delegate { - using (var innerReleaser = await asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser innerReleaser = await asyncLock.WriteLockAsync()) { await Task.WhenAny(onExclusiveLockReleasedBegun.WaitAsync(), Task.Delay(AsyncDelay)); await innerReleaser.ReleaseAsync(); @@ -1327,7 +1326,7 @@ { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - var awaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); try { Assert.False(awaiter.IsCompleted); @@ -1405,7 +1404,7 @@ using (await this.asyncLock.WriteLockAsync()) { Assert.True(this.asyncLock.IsWriteLockHeld); - var nowait = firstLockObtained.SetAsync(); + Task? nowait = firstLockObtained.SetAsync(); await Task.Delay(AsyncDelay); // hold it long enough to ensure our other thread blocks waiting for the read lock. Assert.True(this.asyncLock.IsWriteLockHeld); } @@ -1471,10 +1470,6 @@ Assert.True(firstLockTask.Wait(TestTimeout)); // rethrow any exceptions } -#endregion - -#region WriteLockAsync tests - [Fact] public async Task WriteLockAsync() { @@ -1513,10 +1508,10 @@ { using (await this.asyncLock.UpgradeableReadLockAsync()) { - var nowait = upgradeableLockAcquired.SetAsync(); + Task? nowait = upgradeableLockAcquired.SetAsync(); await readLockAcquired.Task; - var upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(upgradeAwaiter.IsCompleted); // contested lock should not be immediately available. upgradeAwaiter.OnCompleted(delegate { @@ -1535,7 +1530,7 @@ await upgradeableLockAcquired.Task; using (await this.asyncLock.ReadLockAsync()) { - var nowait = readLockAcquired.SetAsync(); + Task? nowait = readLockAcquired.SetAsync(); await writeLockRequested.Task; } })); @@ -1551,10 +1546,10 @@ { using (await this.asyncLock.UpgradeableReadLockAsync()) { - var nowait = upgradeableLockAcquired.SetAsync(); + Task? nowait = upgradeableLockAcquired.SetAsync(); await contendingWriteLockRequested.Task; - var upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.True(upgradeAwaiter.IsCompleted); // the waiting writer should not have priority of this one. upgradeAwaiter.GetResult().Dispose(); // accept and release the lock. } @@ -1562,7 +1557,7 @@ Task.Run(async delegate { await upgradeableLockAcquired.Task; - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted); var contestingWriteLockAcquired = new TaskCompletionSource(); writeAwaiter.OnCompleted(delegate @@ -1572,7 +1567,7 @@ contestingWriteLockAcquired.SetAsync(); } }); - var nowait = contendingWriteLockRequested.SetAsync(); + Task? nowait = contendingWriteLockRequested.SetAsync(); await contestingWriteLockAcquired.Task; })); } @@ -1592,11 +1587,11 @@ using (await this.asyncLock.UpgradeableReadLockAsync()) { this.Logger.WriteLine("Task 1: Acquired an upgradeable read lock."); - var nowait = upgradeableLockAcquired.SetAsync(); + Task? nowait = upgradeableLockAcquired.SetAsync(); await Task.WhenAll(readLockAcquired.Task, contendingWriteLockRequested.Task); this.Logger.WriteLine("Task 1: Requesting a write lock."); - var upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? upgradeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); this.Logger.WriteLine("Task 1: Write lock requested."); Assert.False(upgradeAwaiter.IsCompleted); // contested lock should not be immediately available. upgradeAwaiter.OnCompleted(delegate @@ -1622,7 +1617,7 @@ using (await this.asyncLock.ReadLockAsync()) { this.Logger.WriteLine("Task 2: Acquired read lock."); - var nowait = readLockAcquired.SetAsync(); + Task? nowait = readLockAcquired.SetAsync(); this.Logger.WriteLine("Task 2: Awaiting write lock request by task 1."); await writeLockRequested.Task; this.Logger.WriteLine("Task 2: Releasing read lock."); @@ -1635,7 +1630,7 @@ this.Logger.WriteLine("Task 3: Waiting for upgradeable read lock acquisition in task 1 and read lock acquisition in task 2."); await Task.WhenAll(upgradeableLockAcquired.Task, readLockAcquired.Task); this.Logger.WriteLine("Task 3: Requesting write lock."); - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); this.Logger.WriteLine("Task 3: Write lock requested."); Assert.False(writeAwaiter.IsCompleted); var contestingWriteLockAcquired = new TaskCompletionSource(); @@ -1650,7 +1645,7 @@ this.Logger.WriteLine("Task 3: Write lock released."); }); - var nowait = contendingWriteLockRequested.SetAsync(); + Task? nowait = contendingWriteLockRequested.SetAsync(); this.Logger.WriteLine("Task 3: Awaiting write lock acquisition."); await contestingWriteLockAcquired.Task; this.Logger.WriteLine("Task 3: Write lock acquisition complete."); @@ -1731,7 +1726,7 @@ { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); try { Assert.False(awaiter.IsCompleted); @@ -1813,7 +1808,7 @@ var firstLockToDispose = new AsyncManualResetEvent(); var firstLockTask = Task.Run(async delegate { - using (var firstLock = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser firstLock = await this.asyncLock.WriteLockAsync()) { firstLockAccquired.Set(); await firstLockToRelease.WaitAsync(); @@ -1829,13 +1824,13 @@ var secondLockTask = Task.Run(async delegate { await firstLockAccquired.WaitAsync(); - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted); awaiter.OnCompleted(() => { try { - using (var access = awaiter.GetResult()) + using (AsyncReaderWriterLock.Releaser access = awaiter.GetResult()) { firstLockToDispose.Set(); @@ -1899,7 +1894,7 @@ using (await this.asyncLock.WriteLockAsync()) { Assert.True(this.asyncLock.IsWriteLockHeld); - var nowait = firstLockObtained.SetAsync(); + Task? nowait = firstLockObtained.SetAsync(); await Task.Delay(AsyncDelay); // hold it long enough to ensure our other thread blocks waiting for the read lock. Assert.True(this.asyncLock.IsWriteLockHeld); } @@ -1920,10 +1915,6 @@ })); } -#endregion - -#region Read/write lock interactions - /// Verifies that reads and upgradeable reads can run concurrently. [Fact] public async Task UpgradeableReadAvailableWithExistingReaders() @@ -2000,11 +1991,11 @@ Task.Run(async delegate { await upgradeableReadHeld.Task; - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted, "We shouldn't get a write lock when an upgradeable read is held."); awaiter.OnCompleted(delegate { - using (var releaser = awaiter.GetResult()) + using (AsyncReaderWriterLock.Releaser releaser = awaiter.GetResult()) { writeLockObtained.SetAsync(); } @@ -2027,7 +2018,7 @@ using (await this.asyncLock.UpgradeableReadLockAsync()) { await readerHasLock.Task; - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted, "The upgradeable read lock should not be upgraded while readers still have locks."); awaiter.OnCompleted(delegate { @@ -2130,7 +2121,7 @@ Task.Run(async delegate { await readLockHeld.Task; - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted, "The writer should not be issued a lock while a read lock is held."); this.Logger.WriteLine("Write lock in queue."); writeAwaiter.OnCompleted(delegate @@ -2154,7 +2145,7 @@ Task.Run(async delegate { await writerWaitingForLock.Task; - var readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.False(readAwaiter.IsCompleted, "The new reader should not be issued a lock while a write lock is pending."); this.Logger.WriteLine("Second reader in queue."); readAwaiter.OnCompleted(delegate @@ -2201,7 +2192,7 @@ { // FIRST READER using (await this.asyncLock.ReadLockAsync()) { - var nowait = firstReaderAcquired.SetAsync(); + Task? nowait = firstReaderAcquired.SetAsync(); await releaseFirstReader.Task; } }), @@ -2209,14 +2200,14 @@ { // SECOND READER using (await this.asyncLock.ReadLockAsync()) { - var nowait = secondReaderAcquired.SetAsync(); + Task? nowait = secondReaderAcquired.SetAsync(); await releaseSecondReader.Task; } }), Task.Run(async delegate { // WRITER await Task.WhenAll(firstReaderAcquired.Task, secondReaderAcquired.Task); - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted); writeAwaiter.OnCompleted(delegate { @@ -2226,13 +2217,13 @@ Assert.False(thirdReadAcquired.Task.IsCompleted); } }); - var nowait = writerWaiting.SetAsync(); + Task? nowait = writerWaiting.SetAsync(); await writeAcquired.Task; }), Task.Run(async delegate { // THIRD READER await writerWaiting.Task; - var readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? readAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.False(readAwaiter.IsCompleted, "Third reader should not have been issued a new top-level lock while writer is in the queue."); readAwaiter.OnCompleted(delegate { @@ -2242,13 +2233,13 @@ Assert.True(writeAcquired.Task.IsCompleted); } }); - var nowait = thirdReaderWaiting.SetAsync(); + Task? nowait = thirdReaderWaiting.SetAsync(); await thirdReadAcquired.Task; }), Task.Run(async delegate { // Coordinator await thirdReaderWaiting.Task; - var nowait = releaseFirstReader.SetAsync(); + Task? nowait = releaseFirstReader.SetAsync(); nowait = releaseSecondReader.SetAsync(); })); } @@ -2279,7 +2270,7 @@ Task.Run(async delegate { await readerLockHeld.Task; - var writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writerAwaiter.IsCompleted); writerAwaiter.OnCompleted(delegate { @@ -2303,11 +2294,11 @@ await Task.WhenAll( Task.Run(async delegate { - using (var upgradeableReader = await this.asyncLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterLock.Releaser upgradeableReader = await this.asyncLock.UpgradeableReadLockAsync()) { Assert.True(this.asyncLock.IsUpgradeableReadLockHeld); await firstUpgradeableReadHeld.SetAsync(); - using (var standardReader = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser standardReader = await this.asyncLock.ReadLockAsync()) { Assert.True(this.asyncLock.IsReadLockHeld); Assert.True(this.asyncLock.IsUpgradeableReadLockHeld); @@ -2379,7 +2370,7 @@ using (await this.asyncLock.UpgradeableReadLockAsync()) { await contestingReadLockAcquired.Task; - var writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writeAwaiter.IsCompleted); var nestedLockAcquired = new TaskCompletionSource(); writeAwaiter.OnCompleted(async delegate @@ -2388,11 +2379,11 @@ { using (await this.asyncLock.UpgradeableReadLockAsync()) { - var nowait2 = nestedLockAcquired.SetAsync(); + Task? nowait2 = nestedLockAcquired.SetAsync(); } } }); - var nowait = writeLockWaiting.SetAsync(); + Task? nowait = writeLockWaiting.SetAsync(); await nestedLockAcquired.Task; } }), @@ -2400,16 +2391,12 @@ { using (await this.asyncLock.ReadLockAsync()) { - var nowait = contestingReadLockAcquired.SetAsync(); + Task? nowait = contestingReadLockAcquired.SetAsync(); await writeLockWaiting.Task; } })); } -#endregion - -#region Cancellation tests - [Fact] public async Task PrecancelledReadLockAsyncRequest() { @@ -2417,7 +2404,7 @@ { // get onto an MTA var cts = new CancellationTokenSource(); cts.Cancel(); - var awaiter = this.asyncLock.ReadLockAsync(cts.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync(cts.Token).GetAwaiter(); Assert.True(awaiter.IsCompleted); try { @@ -2435,7 +2422,7 @@ { var cts = new CancellationTokenSource(); cts.Cancel(); - var awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); Assert.True(awaiter.IsCompleted); try { @@ -2465,7 +2452,7 @@ { await firstWriteHeld.Task; var cts = new CancellationTokenSource(); - var awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); Assert.False(awaiter.IsCompleted); awaiter.OnCompleted(delegate { @@ -2503,7 +2490,7 @@ { await firstWriteHeld.Task; var cts = new CancellationTokenSource(); - var awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync(cts.Token).GetAwaiter(); Assert.False(awaiter.IsCompleted); awaiter.OnCompleted(delegate { @@ -2525,7 +2512,7 @@ await cancellationTestConcluded.Task; Assert.False(this.asyncLock.IsWriteLockHeld); Assert.False(this.asyncLock.IsReadLockHeld); - var readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); readLockAwaiter.OnCompleted(delegate { using (readLockAwaiter.GetResult()) @@ -2538,7 +2525,7 @@ Assert.False(this.asyncLock.IsWriteLockHeld); readerConcluded.SetAsync(); }); - var nowait = releaseWriteLock.SetAsync(); + Task? nowait = releaseWriteLock.SetAsync(); await readerConcluded.Task; })); } @@ -2563,8 +2550,8 @@ var lockAwaitFinished = new TaskCompletionSource(); var cts = new CancellationTokenSource(); - var awaitable = this.asyncLock.UpgradeableReadLockAsync(cts.Token); - var awaiter = awaitable.GetAwaiter(); + AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.UpgradeableReadLockAsync(cts.Token); + AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter(); cts.Cancel(); if (awaiter.IsCompleted) @@ -2617,8 +2604,8 @@ var cts = new CancellationTokenSource(); - var awaitable = this.asyncLock.WriteLockAsync(cts.Token); - var awaiter = awaitable.GetAwaiter(); + AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.WriteLockAsync(cts.Token); + AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter(); Assert.False(awaiter.IsCompleted, "The lock should not be issued until read lock is released."); cts.Cancel(); @@ -2647,7 +2634,7 @@ await firstReadLockAcquired.WaitAsync(); var cancellationSource = new CancellationTokenSource(); - var writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); Assert.False(writeLockAwaiter.IsCompleted); writeLockAwaiter.OnCompleted(delegate @@ -2661,7 +2648,7 @@ } }); - var readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); var secondReadLockAcquired = new AsyncManualResetEvent(); Assert.False(readLockAwaiter.IsCompleted); @@ -2696,7 +2683,7 @@ await firstReadLockAcquired.WaitAsync(); var cancellationSource = new CancellationTokenSource(); - var writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); Assert.False(writeLockAwaiter.IsCompleted); writeLockAwaiter.OnCompleted(delegate @@ -2710,7 +2697,7 @@ } }); - var upgradeableReadLockAwaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? upgradeableReadLockAwaiter = this.asyncLock.UpgradeableReadLockAsync().GetAwaiter(); var upgradeableReadLockAcquired = new AsyncManualResetEvent(); Assert.False(upgradeableReadLockAwaiter.IsCompleted); @@ -2746,7 +2733,7 @@ await firstWriteLockAcquired.WaitAsync(); var cancellationSource = new CancellationTokenSource(); - var writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeLockAwaiter = this.asyncLock.WriteLockAsync(cancellationSource.Token).GetAwaiter(); Assert.False(writeLockAwaiter.IsCompleted); writeLockAwaiter.OnCompleted(delegate @@ -2760,7 +2747,7 @@ } }); - var readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? readLockAwaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); var readLockAcquired = new AsyncManualResetEvent(); Assert.False(readLockAwaiter.IsCompleted); @@ -2782,10 +2769,6 @@ await readLockAcquired.WaitAsync(); } - #endregion - - #region Completion tests - [StaFact] public void CompleteBlocksNewTopLevelLocksSTA() { @@ -2795,7 +2778,7 @@ // Exceptions should always be thrown via the awaitable result rather than synchronously thrown // so that we meet expectations of C# async methods. - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.True(awaiter.IsCompleted); try { @@ -2816,7 +2799,7 @@ { // Exceptions should always be thrown via the awaitable result rather than synchronously thrown // so that we meet expectations of C# async methods. - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assert.True(awaiter.IsCompleted); try { @@ -2871,7 +2854,7 @@ try { await firstLockAcquired.Task; - var secondWriteAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? secondWriteAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(secondWriteAwaiter.IsCompleted); this.Logger.WriteLine("Second write lock request pended."); secondWriteAwaiter.OnCompleted(delegate @@ -2902,10 +2885,6 @@ await this.asyncLock.Completion; } -#endregion - -#region Lock callback tests - [Fact] public async Task OnBeforeExclusiveLockReleasedAsyncSimpleSyncHandler() { @@ -3451,7 +3430,7 @@ await callbackFired.Task; try { - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted); await writeLockRequested.SetAsync(); } @@ -3539,7 +3518,7 @@ Task.Run(async delegate { await firstWriteHeld.Task; - var writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writerAwaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(writerAwaiter.IsCompleted); writerAwaiter.OnCompleted(delegate { @@ -3577,10 +3556,10 @@ stub.OnExclusiveLockReleasedAsyncDelegate = () => exclusiveLockReleasedTaskSource.Task; - using (var releaser = await stub.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser = await stub.WriteLockAsync()) { stub.OnBeforeWriteLockReleased(() => beforeWriteLockReleasedTaskSource.Task); - var releaseTask = releaser.ReleaseAsync(); + Task? releaseTask = releaser.ReleaseAsync(); beforeWriteLockReleasedTaskSource.SetResult(null); exclusiveLockReleasedTaskSource.SetResult(null); @@ -3588,10 +3567,6 @@ } } -#endregion - -#region Thread apartment rules - /// Verifies that locks requested on STA threads will marshal to an MTA. [StaFact] public async Task StaLockRequestsMarshalToMTA() @@ -3601,8 +3576,8 @@ { try { - var awaitable = this.asyncLock.ReadLockAsync(); - var awaiter = awaitable.GetAwaiter(); + AsyncReaderWriterLock.Awaitable awaitable = this.asyncLock.ReadLockAsync(); + AsyncReaderWriterLock.Awaiter? awaiter = awaitable.GetAwaiter(); Assert.False(awaiter.IsCompleted, "The lock should not be issued on an STA thread."); awaiter.OnCompleted(delegate @@ -3785,15 +3760,12 @@ } } -#endregion - -#region Lock nesting tests - [Fact] public async Task NestedLocksScenarios() { // R = Reader, U = non-sticky Upgradeable reader, S = Sticky upgradeable reader, W = Writer - var scenarios = new Dictionary { + var scenarios = new Dictionary + { { "RU", false }, // false means this lock sequence should throw at the last step. { "RS", false }, { "RW", false }, @@ -3821,7 +3793,7 @@ { "USUSRWR", true }, }; - foreach (var scenario in scenarios) + foreach (KeyValuePair scenario in scenarios) { this.Logger.WriteLine("Testing {1} scenario: {0}", scenario.Key, scenario.Value ? "valid" : "invalid"); await this.NestedLockHelper(scenario.Key, scenario.Value); @@ -3886,7 +3858,7 @@ this.asyncLock = asyncLock; Task readerTask; - using (var access = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser access = await this.asyncLock.WriteLockAsync()) { asyncLock.OnExclusiveLockReleasedAsyncDelegate = async delegate { @@ -3946,7 +3918,7 @@ this.asyncLock = asyncLock; Task writeLockReleaseTask; - var writerLock = await this.asyncLock.WriteLockAsync(); + AsyncReaderWriterLock.Releaser writerLock = await this.asyncLock.WriteLockAsync(); asyncLock.OnExclusiveLockReleasedAsyncDelegate = async delegate { writeLockCallbackBegun.Set(); @@ -3955,7 +3927,7 @@ await readLockReleased; }; - var readerLock = await this.asyncLock.ReadLockAsync(); + AsyncReaderWriterLock.Releaser readerLock = await this.asyncLock.ReadLockAsync(); // This is really unnatural, to release a lock without awaiting it. // In fact I daresay we could call it illegal. @@ -3989,10 +3961,6 @@ } } -#endregion - -#region Lock data tests - [Fact] public void SetLockDataWithoutLock() { @@ -4033,8 +4001,6 @@ } } -#endregion - [Fact] public async Task DisposeWhileExclusiveLockContextCaptured() { @@ -4054,7 +4020,7 @@ public void GetHangReportSimple() { IHangReportContributor reportContributor = this.asyncLock; - var report = reportContributor.GetHangReport(); + HangReportContribution? report = reportContributor.GetHangReport(); Assert.NotNull(report); Assert.NotNull(report.Content); Assert.NotNull(report.ContentType); @@ -4065,11 +4031,11 @@ [Fact] public async Task GetHangReportWithReleasedNestingOuterLock() { - using (var lock1 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync()) { - using (var lock2 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync()) { - using (var lock3 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock3 = await this.asyncLock.ReadLockAsync()) { await lock1.ReleaseAsync(); this.PrintHangReport(); @@ -4081,11 +4047,11 @@ [Fact] public async Task GetHangReportWithReleasedNestingMiddleLock() { - using (var lock1 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync()) { - using (var lock2 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync()) { - using (var lock3 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock3 = await this.asyncLock.ReadLockAsync()) { await lock2.ReleaseAsync(); this.PrintHangReport(); @@ -4104,7 +4070,7 @@ using (await this.asyncLock.UpgradeableReadLockAsync()) { await readLockObtained; - var writeWaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? writeWaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); writeWaiter.OnCompleted(delegate { writeWaiter.GetResult().Dispose(); @@ -4113,14 +4079,15 @@ hangReportComplete.Set(); } }); - using (var lock1 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock1 = await this.asyncLock.ReadLockAsync()) { - using (var lock2 = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lock2 = await this.asyncLock.ReadLockAsync()) { readLockObtained.Set(); await hangReportComplete; } } + await writerTask; } @@ -4129,7 +4096,7 @@ { var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal(); asyncLocal.Value = "expected"; - using (var lck = await this.asyncLock.ReadLockAsync()) + using (AsyncReaderWriterLock.Releaser lck = await this.asyncLock.ReadLockAsync()) { Assert.Equal("expected", asyncLocal.Value); } @@ -4140,7 +4107,7 @@ { var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal(); asyncLocal.Value = "expected"; - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assumes.False(awaiter.IsCompleted); var testResultSource = new TaskCompletionSource(); awaiter.OnCompleted(delegate @@ -4169,7 +4136,7 @@ { var asyncLocal = new Microsoft.VisualStudio.Threading.AsyncLocal(); asyncLocal.Value = "expected"; - var awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.ReadLockAsync().GetAwaiter(); Assumes.False(awaiter.IsCompleted); var testResultSource = new TaskCompletionSource(); awaiter.UnsafeOnCompleted(delegate @@ -4203,6 +4170,7 @@ using (await asyncLock.ReadLockAsync()) { } + Assumes.Equals(0, asyncLock.StartedTaskCount); var writeLockObtained = new AsyncManualResetEvent(); @@ -4255,7 +4223,7 @@ private void PrintHangReport() { IHangReportContributor reportContributor = this.asyncLock; - var report = reportContributor.GetHangReport(); + HangReportContribution? report = reportContributor.GetHangReport(); this.Logger.WriteLine(report.Content); } @@ -4281,7 +4249,7 @@ Task.Run(async delegate { await initialLockHeld.Task; - var awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); + AsyncReaderWriterLock.Awaiter? awaiter = this.asyncLock.WriteLockAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted); awaiter.OnCompleted(delegate { @@ -4631,7 +4599,7 @@ Assert.Equal(hasWriteLock, this.asyncLock.IsWriteLockHeld); }; - var workerTask = worker(); + Task? workerTask = worker(); Thread.Sleep(AsyncDelay); // give the worker plenty of time to execute if it's going to. (we don't expect it) Assert.False(workerTask.IsCompleted); @@ -4655,7 +4623,7 @@ var cancellation = CancellationTokenSource.CreateLinkedTokenSource( overallCancellation.Token, new CancellationTokenSource(iterationTimeout).Token); - var token = testCancellation ? cancellation.Token : CancellationToken.None; + CancellationToken token = testCancellation ? cancellation.Token : CancellationToken.None; Func worker = async delegate { @@ -4741,7 +4709,7 @@ cancellation.Cancel(); } - var releaser = lockStack.Pop(); + AsyncReaderWriterLock.Releaser releaser = lockStack.Pop(); log += '_'; releaser.Dispose(); } @@ -4755,7 +4723,7 @@ for (int i = 0; i < workers.Length; i++) { workers[i] = Task.Run(() => worker(), cancellation.Token); - var nowait = workers[i].ContinueWith(_ => cancellation.Cancel(), TaskContinuationOptions.OnlyOnFaulted); + Task? nowait = workers[i].ContinueWith(_ => cancellation.Cancel(), TaskContinuationOptions.OnlyOnFaulted); } try @@ -4895,14 +4863,14 @@ { get { - var ambient = this.AmbientLock; + LockHandle ambient = this.AmbientLock; return new InternalLockHandle(ambient.IsUpgradeableReadLock, ambient.IsWriteLock); } } internal void SetLockData(object? data) { - var lck = this.AmbientLock; + LockHandle lck = this.AmbientLock; lck.Data = data; } @@ -4927,7 +4895,7 @@ { await Task.Yield(); await base.OnBeforeExclusiveLockReleasedAsync(); - if (this.OnBeforeExclusiveLockReleasedAsyncDelegate != null) + if (this.OnBeforeExclusiveLockReleasedAsyncDelegate is object) { await this.OnBeforeExclusiveLockReleasedAsyncDelegate(); } @@ -4936,7 +4904,7 @@ protected override async Task OnExclusiveLockReleasedAsync() { await base.OnExclusiveLockReleasedAsync(); - if (this.OnExclusiveLockReleasedAsyncDelegate != null) + if (this.OnExclusiveLockReleasedAsyncDelegate is object) { await this.OnExclusiveLockReleasedAsyncDelegate(); } @@ -4945,7 +4913,7 @@ protected override async Task OnBeforeLockReleasedAsync(bool exclusiveLockRelease, LockHandle releasingLock) { await base.OnBeforeLockReleasedAsync(exclusiveLockRelease, releasingLock); - if (this.OnBeforeLockReleasedAsyncDelegate != null) + if (this.OnBeforeLockReleasedAsyncDelegate is object) { await this.OnBeforeLockReleasedAsyncDelegate(); } @@ -5053,7 +5021,7 @@ { var tuple = (Tuple)s!; Interlocked.Increment(ref tuple.Item1.startedTaskCount); - var originalContext = SynchronizationContext.Current; + SynchronizationContext? originalContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(this.SynchronizationContext); tuple.Item1.TryExecuteTask(tuple.Item2); SynchronizationContext.SetSynchronizationContext(originalContext); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs similarity index 78% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs index 7a12a18a..d0949a99 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterResourceLockTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -14,9 +17,10 @@ { private const char ReadChar = 'R'; private const char UpgradeableReadChar = 'U'; - private const char StickyUpgradeableReadChar = 'S'; private const char WriteChar = 'W'; - private static bool verboseLogEnabled = false; +#pragma warning disable CS0649 // field never assigned to + private static bool verboseLogEnabled; +#pragma warning restore CS0649 // field never assigned to private ResourceLockWrapper resourceLock; @@ -35,9 +39,9 @@ [Fact] public async Task ReadResourceAsync() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -47,9 +51,9 @@ [Fact] public async Task UpgradeableReadResourceAsync() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -59,17 +63,17 @@ [Fact] public async Task WriteResourceAsync() { - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); } - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(2, resource.ExclusiveAccessPreparationCount); @@ -79,9 +83,9 @@ [Fact] public async Task PreparationExecutesJustOncePerReadLock() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -89,7 +93,7 @@ Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.ReadLockAsync()) { await access2.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); @@ -101,9 +105,9 @@ [Fact] public async Task PreparationExecutesJustOncePerUpgradeableReadLock() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -111,7 +115,7 @@ Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.UpgradeableReadLockAsync()) { await access2.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); @@ -123,9 +127,9 @@ [Fact] public async Task PreparationExecutesJustOncePerWriteLock() { - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); @@ -133,7 +137,7 @@ Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { await access2.GetResourceAsync(1); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); @@ -148,9 +152,9 @@ [Fact] public async Task PreparationSkippedForWriteLockWithFlag() { - using (var access = await this.resourceLock.WriteLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -161,9 +165,9 @@ [Fact] public async Task PreparationNotSkippedForUpgradeableReadLockWithFlag() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -173,11 +177,11 @@ [Fact] public async Task PreparationSkippedForWriteLockUnderUpgradeableReadWithFlag() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.SkipInitialPreparation)) { - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { - var resource = await access2.GetResourceAsync(1); + Resource? resource = await access2.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -189,15 +193,15 @@ [Fact] public async Task PreparationSwitchesFromExclusiveToConcurrent() { - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Exclusive, resource.CurrentState); } - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Concurrent, resource.CurrentState); } } @@ -205,15 +209,15 @@ [Fact] public async Task PreparationSwitchesFromConcurrentToExclusive() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Concurrent, resource.CurrentState); } - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Exclusive, resource.CurrentState); } } @@ -221,27 +225,27 @@ [Fact] public async Task PreparationSwitchesWithSkipInitialPreparation() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Concurrent, resource.CurrentState); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); } // Obtain a resource via a write lock with SkipInitialPreparation on. - using (var writeAccess = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser writeAccess = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) { - var resource = await writeAccess.GetResourceAsync(1); + Resource? resource = await writeAccess.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationSkippedCount); Assert.Equal(Resource.State.Concurrent, resource.CurrentState); } - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Equal(Resource.State.Concurrent, resource.CurrentState); Assert.Equal(2, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); @@ -252,7 +256,7 @@ [Fact] public async Task PreparationOccursForEachTopLevelExclusiveWrite() { - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { await access.GetResourceAsync(1); Assert.Equal(0, this.resources[1].ConcurrentAccessPreparationCount); @@ -262,7 +266,7 @@ Assert.Equal(0, this.resources[2].ExclusiveAccessPreparationCount); } - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { // Although the resource was already prepared for exclusive access, each exclusive access // is its own entity and requires preparation. In particular the CPS ProjectLockService @@ -279,7 +283,7 @@ Assert.Equal(0, this.resources[2].ConcurrentAccessPreparationCount); Assert.Equal(1, this.resources[2].ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { // This is the same top-level exclusive lock, so preparation should *not* occur a 3rd time. await access2.GetResourceAsync(1); @@ -300,9 +304,9 @@ var reader2Waiting = new AsyncManualResetEvent(); var reader1 = Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resourceTask = access.GetResourceAsync(1, cts.Token); + Task? resourceTask = access.GetResourceAsync(1, cts.Token); Assert.False(resourceTask.IsCompleted); reader1Waiting.Set(); try @@ -317,12 +321,12 @@ }); var reader2 = Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resourceTask = access.GetResourceAsync(1); + Task? resourceTask = access.GetResourceAsync(1); Assert.False(resourceTask.IsCompleted); reader2Waiting.Set(); - var resource = await resourceTask; + Resource? resource = await resourceTask; Assert.Same(resource, this.resources[1]); } }); @@ -343,13 +347,13 @@ [Fact] public async Task ResourceHeldByUpgradeableReadPreparedWhenWriteLockReleasedWithoutResource() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { await access.GetResourceAsync(1); Assert.Equal(1, this.resources[1].ConcurrentAccessPreparationCount); Assert.Equal(0, this.resources[1].ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { } @@ -371,17 +375,17 @@ [Fact] public async Task ResourceHeldByUpgradeableReadPreparedWhenWriteLockReleased() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); Resource resource2; - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { - var resource1Again = await access2.GetResourceAsync(1); + Resource? resource1Again = await access2.GetResourceAsync(1); Assert.Same(resource, resource1Again); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); @@ -405,7 +409,7 @@ [Fact] public async Task PreparationIsAppliedToResourceImpactedByOutsideChange() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { this.resourceLock.SetResourceAsAccessed(this.resources[1]); await access.GetResourceAsync(2); @@ -416,7 +420,7 @@ Assert.Equal(0, this.resources[2].ExclusiveAccessPreparationCount); } - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { this.resourceLock.SetResourceAsAccessed(this.resources[1]); await access.GetResourceAsync(2); @@ -432,7 +436,7 @@ Assert.Equal(1, this.resources[2].ConcurrentAccessPreparationCount); Assert.Equal(1, this.resources[2].ExclusiveAccessPreparationCount); - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { Assert.Equal(0, this.resources[1].ConcurrentAccessPreparationCount); Assert.Equal(0, this.resources[1].ExclusiveAccessPreparationCount); @@ -452,15 +456,17 @@ [Fact] public async Task PreparationIsAppliedToResourceImpactedByOutsideChangePredicate() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { object state = new object(); - this.resourceLock.SetResourceAsAccessed((resource, s) => - { - Assert.Same(state, s); - Assert.True(false, "Read locks should not invoke this."); - return false; - }, state); + this.resourceLock.SetResourceAsAccessed( + (resource, s) => + { + Assert.Same(state, s); + Assert.True(false, "Read locks should not invoke this."); + return false; + }, + state); await access.GetResourceAsync(2); Assert.Equal(0, this.resources[1].ConcurrentAccessPreparationCount); @@ -469,7 +475,7 @@ Assert.Equal(0, this.resources[2].ExclusiveAccessPreparationCount); } - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { this.resourceLock.SetResourceAsAccessed((resource, state) => resource == this.resources[1], null); await access.GetResourceAsync(2); @@ -485,7 +491,7 @@ Assert.Equal(1, this.resources[2].ConcurrentAccessPreparationCount); Assert.Equal(1, this.resources[2].ExclusiveAccessPreparationCount); - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { Assert.Equal(0, this.resources[1].ConcurrentAccessPreparationCount); Assert.Equal(0, this.resources[1].ExclusiveAccessPreparationCount); @@ -505,7 +511,7 @@ [Fact] public async Task PreparationIsAssumedUnknownForAllResourcesAfterExclusiveLockReleased() { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { await access.GetResourceAsync(1); await access.GetResourceAsync(2); @@ -516,7 +522,7 @@ Assert.Equal(1, this.resources[2].ConcurrentAccessPreparationCount); Assert.Equal(0, this.resources[2].ExclusiveAccessPreparationCount); - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { } @@ -525,7 +531,7 @@ Assert.Equal(1, this.resources[2].ConcurrentAccessPreparationCount); Assert.Equal(0, this.resources[2].ExclusiveAccessPreparationCount); - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { // Although the write lock above did not explicitly request access to this // resource, requesting access is just a convenience. If the resource is available @@ -548,16 +554,16 @@ [Fact] public async Task ResourceHeldByStickyUpgradeableReadNotPreparedWhenExplicitWriteLockReleased() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.StickyWrite)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync(AsyncReaderWriterResourceLock.LockFlags.StickyWrite)) { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.Same(this.resources[1], resource); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { - var resource1Again = await access2.GetResourceAsync(1); + Resource? resource1Again = await access2.GetResourceAsync(1); Assert.Same(resource, resource1Again); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); @@ -577,10 +583,10 @@ [Fact] public async Task DowngradedWriteLockDoesNotPrepareResourceWhenUpgradeableReadDidNotHaveIt() { - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { Resource resource; - using (var access2 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync()) { resource = await access2.GetResourceAsync(1); Assert.Same(this.resources[1], resource); @@ -592,7 +598,7 @@ Assert.Equal(0, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); - var readResource = await access.GetResourceAsync(1); + Resource? readResource = await access.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(1, resource.ExclusiveAccessPreparationCount); } @@ -606,22 +612,22 @@ { var resourceTask1 = new TaskCompletionSource(); var resourceTask2 = new TaskCompletionSource(); - var preparationEnteredTask1 = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask1.Task); - var preparationEnteredTask2 = this.resourceLock.SetPreparationTask(this.resources[2], resourceTask2.Task); + Task? preparationEnteredTask1 = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask1.Task); + Task? preparationEnteredTask2 = this.resourceLock.SetPreparationTask(this.resources[2], resourceTask2.Task); await Task.WhenAll( Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource1 = await access.GetResourceAsync(1); + Resource? resource1 = await access.GetResourceAsync(1); } }), Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource2 = await access.GetResourceAsync(2); + Resource? resource2 = await access.GetResourceAsync(2); } }), Task.Run(async delegate @@ -642,25 +648,25 @@ public async Task IndividualResourcePreparationNotConcurrent() { var resourceTask = new TaskCompletionSource(); - var preparationEnteredTask1 = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task); + Task? preparationEnteredTask1 = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task); var requestSubmitted1 = new TaskCompletionSource(); var requestSubmitted2 = new TaskCompletionSource(); await Task.WhenAll( Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = access.GetResourceAsync(1); + Task? resource = access.GetResourceAsync(1); requestSubmitted1.SetResult(null); await resource; } }), Task.Run(async delegate { - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { - var resource = access.GetResourceAsync(1); + Task? resource = access.GetResourceAsync(1); requestSubmitted2.SetResult(null); await resource; } @@ -705,10 +711,10 @@ public async Task PreparationReservesLock() { var resourceTask = new TaskCompletionSource(); - var nowait = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task); + Task? nowait = this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task); Task resource; - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { resource = access.GetResourceAsync(1); } @@ -729,9 +735,9 @@ { Resource resource; - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { - using (var access1 = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access1 = await this.resourceLock.WriteLockAsync()) { resource = await access1.GetResourceAsync(1); @@ -748,9 +754,9 @@ Assert.Equal(Resource.State.Exclusive, resource.CurrentState); Assert.Equal(2, resource.ExclusiveAccessPreparationCount); - using (var access2 = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access2 = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) { - var resource2 = await access2.GetResourceAsync(1); + Resource? resource2 = await access2.GetResourceAsync(1); Assert.Same(resource, resource2); Assert.Equal(Resource.State.Exclusive, resource2.CurrentState); @@ -760,9 +766,9 @@ this.resourceLock.SetAllResourcesToUnknownState(); resource.CurrentState = Resource.State.None; - using (var access3 = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) + using (AsyncReaderWriterResourceLock.ResourceReleaser access3 = await this.resourceLock.WriteLockAsync(ResourceLockWrapper.LockFlags.SkipInitialPreparation)) { - var resource3 = await access3.GetResourceAsync(1); + Resource? resource3 = await access3.GetResourceAsync(1); Assert.Same(resource, resource3); Assert.Equal(Resource.State.None, resource3.CurrentState); @@ -778,13 +784,13 @@ [Fact] public async Task AsyncReleaseOfWriteToUpgradeableReadLock() { - using (var upgradeableReadAccess = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser upgradeableReadAccess = await this.resourceLock.UpgradeableReadLockAsync()) { - var resource = await upgradeableReadAccess.GetResourceAsync(1); + Resource? resource = await upgradeableReadAccess.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); Assert.Equal(0, resource.ExclusiveAccessPreparationCount); - using (var writeAccess = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser writeAccess = await this.resourceLock.WriteLockAsync()) { resource = await writeAccess.GetResourceAsync(1); Assert.Equal(1, resource.ConcurrentAccessPreparationCount); @@ -807,13 +813,13 @@ [Fact] public async Task LockReleaseAsyncWithoutWaitFollowedByDispose() { - using (var upgradeableReadAccess = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser upgradeableReadAccess = await this.resourceLock.UpgradeableReadLockAsync()) { - var resource1 = await upgradeableReadAccess.GetResourceAsync(1); - using (var writeAccess = await this.resourceLock.WriteLockAsync()) + Resource? resource1 = await upgradeableReadAccess.GetResourceAsync(1); + using (AsyncReaderWriterResourceLock.ResourceReleaser writeAccess = await this.resourceLock.WriteLockAsync()) { - var resource2 = await writeAccess.GetResourceAsync(1); // the test is to NOT await on this result. - var nowait = writeAccess.ReleaseAsync(); + Resource? resource2 = await writeAccess.GetResourceAsync(1); // the test is to NOT await on this result. + Task? nowait = writeAccess.ReleaseAsync(); } // this calls writeAccess.Dispose(); } } @@ -855,7 +861,7 @@ resourceTask.SetException(new ApplicationException()); this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task).Forget(); - using (var access = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.WriteLockAsync()) { try { @@ -881,11 +887,11 @@ resourceTask.SetException(new ApplicationException()); this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task).Forget(); - using (var access = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.ReadLockAsync()) { try { - var resource = await access.GetResourceAsync(1); + Resource? resource = await access.GetResourceAsync(1); Assert.True(false, "Expected exception not thrown."); } catch (ApplicationException) @@ -904,13 +910,13 @@ public async Task PrepareResourceForConcurrentAccessAsync_ThrowsReleasingWriteShouldNotLeakLock() { TaskCompletionSource resourceTask; - using (var access = await this.resourceLock.UpgradeableReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser access = await this.resourceLock.UpgradeableReadLockAsync()) { await access.GetResourceAsync(1); try { - using (var writeAccess = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser writeAccess = await this.resourceLock.WriteLockAsync()) { resourceTask = new TaskCompletionSource(); resourceTask.SetException(new ApplicationException()); @@ -947,7 +953,7 @@ resourceTask = new TaskCompletionSource(); resourceTask.SetException(new ApplicationException()); this.resourceLock.SetPreparationTask(this.resources[1], resourceTask.Task).Forget(); - using (var readAccess = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser readAccess = await this.resourceLock.ReadLockAsync()) { try { @@ -962,14 +968,14 @@ } // Ensure another write lock can be issued, and can acquire the resource to "fix" it. - using (var writeAccess = await this.resourceLock.WriteLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser writeAccess = await this.resourceLock.WriteLockAsync()) { - var resource = await writeAccess.GetResourceAsync(1); + Resource? resource = await writeAccess.GetResourceAsync(1); Assert.NotNull(resource); } // Finally, verify that the fix was effective. - using (var readAccess = await this.resourceLock.ReadLockAsync()) + using (AsyncReaderWriterResourceLock.ResourceReleaser readAccess = await this.resourceLock.ReadLockAsync()) { await readAccess.GetResourceAsync(1); } @@ -996,7 +1002,7 @@ Assert.True(this.resourceLock.CaptureDiagnostics); // For a sanity check, test basic functionality. - using (var lck = await this.resourceLock.ReadLockAsync(this.TimeoutToken)) + using (AsyncReaderWriterResourceLock.ResourceReleaser lck = await this.resourceLock.ReadLockAsync(this.TimeoutToken)) { } } @@ -1028,7 +1034,7 @@ var cancellation = CancellationTokenSource.CreateLinkedTokenSource( overallCancellation.Token, new CancellationTokenSource(iterationTimeout).Token); - var token = testCancellation ? cancellation.Token : CancellationToken.None; + CancellationToken token = testCancellation ? cancellation.Token : CancellationToken.None; Func worker = async workerId => { @@ -1104,11 +1110,11 @@ break; } - var expectedState = this.resourceLock.IsWriteLockHeld ? Resource.State.Exclusive : Resource.State.Concurrent; + Resource.State expectedState = this.resourceLock.IsWriteLockHeld ? Resource.State.Exclusive : Resource.State.Concurrent; int resourceIndex = random.Next(maxResources) + 1; this.VerboseLog("Worker {0} is requesting resource {1}, expects {2}", workerId, resourceIndex, expectedState); - var resource = await lockStack.Peek().GetResourceAsync(resourceIndex); - var currentState = resource.CurrentState; + Resource? resource = await lockStack.Peek().GetResourceAsync(resourceIndex); + Resource.State currentState = resource.CurrentState; this.VerboseLog("Worker {0} has received resource {1}, as {2}", workerId, resourceIndex, currentState); Assert.Equal(expectedState, currentState); await Task.Delay(random.Next(maxLockHeldDelay)); @@ -1123,7 +1129,7 @@ cancellation.Cancel(); } - var releaser = lockStack.Pop(); + AsyncReaderWriterResourceLock.ResourceReleaser releaser = lockStack.Pop(); log += '_'; releaser.Dispose(); } @@ -1146,7 +1152,7 @@ { int scopedWorkerId = i; workers[i] = Task.Run(() => worker(scopedWorkerId), cancellation.Token); - var nowait = workers[i].ContinueWith(_ => cancellation.Cancel(), TaskContinuationOptions.OnlyOnFaulted); + Task? nowait = workers[i].ContinueWith(_ => cancellation.Cancel(), TaskContinuationOptions.OnlyOnFaulted); } try @@ -1293,7 +1299,7 @@ } } - if (tuple != null) + if (tuple is object) { tuple.Item1.SetResult(null); // signal that the preparation method has been entered await tuple.Item2; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs similarity index 85% rename from src/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs index efbe2bb9..47dd7107 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncSemaphoreTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -46,7 +49,7 @@ releasers[j] = await this.lck.EnterAsync(); } - var releaseSequence = Enumerable.Range(0, initialCount); + IEnumerable? releaseSequence = Enumerable.Range(0, initialCount); // We'll test both releasing in FIFO and LIFO order. if (i % 2 == 0) @@ -138,11 +141,11 @@ public async Task ContestedAndCancelled() { var cts = new CancellationTokenSource(); - var first = this.lck.EnterAsync(); - var second = this.lck.EnterAsync(cts.Token); + Task? first = this.lck.EnterAsync(); + Task? second = this.lck.EnterAsync(cts.Token); Assert.False(second.IsCompleted); cts.Cancel(); - var ex = await Assert.ThrowsAnyAsync(() => second); + OperationCanceledException? ex = await Assert.ThrowsAnyAsync(() => second); Assert.Equal(cts.Token, ex.CancellationToken); } @@ -150,8 +153,8 @@ public async Task ContestedAndCancelledWithTimeoutSpecified() { var cts = new CancellationTokenSource(); - var first = this.lck.EnterAsync(); - var second = this.lck.EnterAsync(Timeout.Infinite, cts.Token); + Task? first = this.lck.EnterAsync(); + Task? second = this.lck.EnterAsync(Timeout.Infinite, cts.Token); Assert.False(second.IsCompleted); cts.Cancel(); try @@ -219,30 +222,30 @@ [Fact] public void TimeoutIntImmediateFailure() { - var first = this.lck.EnterAsync(0); - var second = this.lck.EnterAsync(0); + Task? first = this.lck.EnterAsync(0); + Task? second = this.lck.EnterAsync(0); Assert.Equal(TaskStatus.Canceled, second.Status); } [Fact] public async Task TimeoutIntEventualFailure() { - var first = this.lck.EnterAsync(0); + Task? first = this.lck.EnterAsync(0); var cts = new CancellationTokenSource(); - var second = this.lck.EnterAsync(100, cts.Token); + Task? second = this.lck.EnterAsync(100, cts.Token); Assert.False(second.IsCompleted); // We expect second to complete and throw OperationCanceledException, // but we add WithTimeout here to prevent a test hang if it fails to do so. - var ex = await Assert.ThrowsAnyAsync(() => second).WithTimeout(TimeSpan.FromMilliseconds(TestTimeout)); + OperationCanceledException? ex = await Assert.ThrowsAnyAsync(() => second).WithTimeout(TimeSpan.FromMilliseconds(TestTimeout)); Assert.NotEqual(cts.Token, ex.CancellationToken); } [Fact] public async Task TimeoutIntSuccess() { - var first = this.lck.EnterAsync(0); - var second = this.lck.EnterAsync(AsyncDelay); + Task? first = this.lck.EnterAsync(0); + Task? second = this.lck.EnterAsync(AsyncDelay); Assert.False(second.IsCompleted); first.Result.Dispose(); await second; @@ -252,8 +255,8 @@ [Fact] public void TimeoutTimeSpan() { - var first = this.lck.EnterAsync(TimeSpan.Zero); - var second = this.lck.EnterAsync(TimeSpan.Zero); + Task? first = this.lck.EnterAsync(TimeSpan.Zero); + Task? second = this.lck.EnterAsync(TimeSpan.Zero); Assert.Equal(TaskStatus.Canceled, second.Status); } @@ -261,9 +264,9 @@ public void TwoResourceSemaphore() { var sem = new AsyncSemaphore(2); - var first = sem.EnterAsync(); - var second = sem.EnterAsync(); - var third = sem.EnterAsync(); + Task? first = sem.EnterAsync(); + Task? second = sem.EnterAsync(); + Task? third = sem.EnterAsync(); Assert.Equal(TaskStatus.RanToCompletion, first.Status); Assert.Equal(TaskStatus.RanToCompletion, second.Status); @@ -285,7 +288,7 @@ } // After requesting another beyond its capacity, it should still report 0. - var extraReleaser = sem.EnterAsync(); + Task? extraReleaser = sem.EnterAsync(); Assert.Equal(0, sem.CurrentCount); for (int i = 0; i < initialCapacity; i++) @@ -301,7 +304,7 @@ [Fact] public async Task TooManyReleases_SameStruct() { - var releaser = await this.lck.EnterAsync(); + AsyncSemaphore.Releaser releaser = await this.lck.EnterAsync(); releaser.Dispose(); releaser.Dispose(); Assert.Equal(2, this.lck.CurrentCount); @@ -310,8 +313,8 @@ [Fact] public async Task TooManyReleases_CopyOfStruct_OverInitialCount() { - var releaser = await this.lck.EnterAsync(); - var releaserCopy = releaser; + AsyncSemaphore.Releaser releaser = await this.lck.EnterAsync(); + AsyncSemaphore.Releaser releaserCopy = releaser; releaser.Dispose(); Assert.Equal(1, this.lck.CurrentCount); @@ -323,11 +326,11 @@ public async Task TooManyReleases_CopyOfStruct() { var sem = new AsyncSemaphore(2); - var releaser1 = await sem.EnterAsync(); - var releaser2 = await sem.EnterAsync(); + AsyncSemaphore.Releaser releaser1 = await sem.EnterAsync(); + AsyncSemaphore.Releaser releaser2 = await sem.EnterAsync(); // Assigning the releaser struct to another local variable copies it. - var releaser2Copy = releaser2; + AsyncSemaphore.Releaser releaser2Copy = releaser2; // Dispose of each copy of the releaser. // The double-release is undetectable. The semaphore should be back at capacity 2. @@ -358,7 +361,7 @@ async delegate { var cts = new CancellationTokenSource(); - var enterTask = sem.EnterAsync(cts.Token); + Task? enterTask = sem.EnterAsync(cts.Token); cts.Cancel(); await enterTask.NoThrowAwaitable(); }, @@ -424,7 +427,7 @@ await this.CheckGCPressureAsync( async delegate { - var blockingReleaser = await sem.EnterAsync(); + AsyncSemaphore.Releaser blockingReleaser = await sem.EnterAsync(); for (int i = 0; i < releasers.Length; i++) { releasers[i] = sem.EnterAsync(); @@ -486,20 +489,20 @@ [Fact] public async Task CancelledRequestIsImmediate() { - var releaser1 = await this.lck.EnterAsync(); + AsyncSemaphore.Releaser releaser1 = await this.lck.EnterAsync(); // With the semaphore fully occupied, issue a cancelable request. var cts = new CancellationTokenSource(); - var releaser2Task = this.lck.EnterAsync(cts.Token); + Task? releaser2Task = this.lck.EnterAsync(cts.Token); Assert.False(releaser2Task.IsCompleted); // Also pend a 3rd request. - var releaser3Task = this.lck.EnterAsync(); + Task? releaser3Task = this.lck.EnterAsync(); Assert.False(releaser3Task.IsCompleted); // Cancel the second user cts.Cancel(); - var oce = await Assert.ThrowsAnyAsync(() => releaser2Task).WithCancellation(this.TimeoutToken); + OperationCanceledException? oce = await Assert.ThrowsAnyAsync(() => releaser2Task).WithCancellation(this.TimeoutToken); Assert.Equal(cts.Token, oce.CancellationToken); // Verify that the 3rd user still hasn't gotten in. @@ -534,7 +537,7 @@ [Fact] public async Task SemaphoreAwaitersAreQueued() { - var holder = await this.lck.EnterAsync(); + AsyncSemaphore.Releaser holder = await this.lck.EnterAsync(); const int waiterCount = 5; var cts = new CancellationTokenSource[waiterCount]; @@ -586,16 +589,16 @@ [Fact] public async Task EnterAsync_TimeSpan_AfterDisposal() { - var first = this.lck.EnterAsync(UnexpectedTimeout); + Task? first = this.lck.EnterAsync(UnexpectedTimeout); Assert.Equal(TaskStatus.RanToCompletion, first.Status); - var second = this.lck.EnterAsync(UnexpectedTimeout); + Task? second = this.lck.EnterAsync(UnexpectedTimeout); Assert.False(second.IsCompleted); this.lck.Dispose(); // Verify that future EnterAsync's fail synchronously. - var third = this.lck.EnterAsync(UnexpectedTimeout); + Task? third = this.lck.EnterAsync(UnexpectedTimeout); Assert.True(third.IsFaulted); Assert.IsType(third.Exception!.InnerException); @@ -625,16 +628,16 @@ [Fact] public async Task EnterAsync_int_AfterDisposal() { - var first = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); + Task? first = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); Assert.Equal(TaskStatus.RanToCompletion, first.Status); - var second = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); + Task? second = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); Assert.False(second.IsCompleted); this.lck.Dispose(); // Verify that future EnterAsync's fail synchronously. - var third = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); + Task? third = this.lck.EnterAsync((int)UnexpectedTimeout.TotalMilliseconds); Assert.True(third.IsFaulted); Assert.IsType(third.Exception!.InnerException); @@ -650,16 +653,16 @@ [Fact] public async Task EnterAsync_AfterDisposal() { - var first = this.lck.EnterAsync(); + Task? first = this.lck.EnterAsync(); Assert.Equal(TaskStatus.RanToCompletion, first.Status); - var second = this.lck.EnterAsync(); + Task? second = this.lck.EnterAsync(); Assert.False(second.IsCompleted); this.lck.Dispose(); // Verify that future EnterAsync's fail synchronously. - var third = this.lck.EnterAsync(); + Task? third = this.lck.EnterAsync(); Assert.True(third.IsFaulted); Assert.IsType(third.Exception!.InnerException); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs index 99cfb843..354b7683 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AwaitExtensionsTests.cs @@ -1,8 +1,5 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { @@ -25,7 +22,7 @@ namespace Microsoft.VisualStudio.Threading.Tests { } - public enum NamedSyncContexts + public enum NamedSyncContext { None, A, @@ -192,7 +189,8 @@ namespace Microsoft.VisualStudio.Threading.Tests // In some test runs (including VSTS cloud test), this test runs on a threadpool thread. if (Thread.CurrentThread.IsThreadPoolThread) { - var testResult = Task.Factory.StartNew(delegate + Task? testResult = Task.Factory.StartNew( + delegate { Assert.False(Thread.CurrentThread.IsThreadPoolThread); // avoid infinite recursion if it doesn't get us off a threadpool thread. this.AwaitThreadPoolSchedulerYieldsOnNonThreadPoolThreads(); @@ -219,25 +217,25 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory] [CombinatorialData] - public void ConfigureAwaitRunInline_NoExtraThreadSwitching(NamedSyncContexts invokeOn, NamedSyncContexts completeOn) + public void ConfigureAwaitRunInline_NoExtraThreadSwitching(NamedSyncContext invokeOn, NamedSyncContext completeOn) { // Set up various SynchronizationContexts that we may invoke or complete the async method with. - var aSyncContext = SingleThreadedTestSynchronizationContext.New(); - var bSyncContext = SingleThreadedTestSynchronizationContext.New(); - var invokeOnSyncContext = invokeOn == NamedSyncContexts.None ? null - : invokeOn == NamedSyncContexts.A ? aSyncContext - : invokeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? aSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? bSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? invokeOnSyncContext = invokeOn == NamedSyncContext.None ? null + : invokeOn == NamedSyncContext.A ? aSyncContext + : invokeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(invokeOn)); - var completeOnSyncContext = completeOn == NamedSyncContexts.None ? null - : completeOn == NamedSyncContexts.A ? aSyncContext - : completeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? completeOnSyncContext = completeOn == NamedSyncContext.None ? null + : completeOn == NamedSyncContext.A ? aSyncContext + : completeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(completeOn)); // Set up a single-threaded SynchronizationContext that we'll invoke the async method within. SynchronizationContext.SetSynchronizationContext(invokeOnSyncContext); var unblockAsyncMethod = new TaskCompletionSource(); - var asyncTask = AwaitThenGetThreadAsync(unblockAsyncMethod.Task); + Task? asyncTask = AwaitThenGetThreadAsync(unblockAsyncMethod.Task); SynchronizationContext.SetSynchronizationContext(completeOnSyncContext); unblockAsyncMethod.SetResult(true); @@ -255,25 +253,25 @@ namespace Microsoft.VisualStudio.Threading.Tests [Theory] [CombinatorialData] - public void ConfigureAwaitRunInlineOfT_NoExtraThreadSwitching(NamedSyncContexts invokeOn, NamedSyncContexts completeOn) + public void ConfigureAwaitRunInlineOfT_NoExtraThreadSwitching(NamedSyncContext invokeOn, NamedSyncContext completeOn) { // Set up various SynchronizationContexts that we may invoke or complete the async method with. - var aSyncContext = SingleThreadedTestSynchronizationContext.New(); - var bSyncContext = SingleThreadedTestSynchronizationContext.New(); - var invokeOnSyncContext = invokeOn == NamedSyncContexts.None ? null - : invokeOn == NamedSyncContexts.A ? aSyncContext - : invokeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? aSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? bSyncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? invokeOnSyncContext = invokeOn == NamedSyncContext.None ? null + : invokeOn == NamedSyncContext.A ? aSyncContext + : invokeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(invokeOn)); - var completeOnSyncContext = completeOn == NamedSyncContexts.None ? null - : completeOn == NamedSyncContexts.A ? aSyncContext - : completeOn == NamedSyncContexts.B ? bSyncContext + SynchronizationContext? completeOnSyncContext = completeOn == NamedSyncContext.None ? null + : completeOn == NamedSyncContext.A ? aSyncContext + : completeOn == NamedSyncContext.B ? bSyncContext : throw new ArgumentOutOfRangeException(nameof(completeOn)); // Set up a single-threaded SynchronizationContext that we'll invoke the async method within. SynchronizationContext.SetSynchronizationContext(invokeOnSyncContext); var unblockAsyncMethod = new TaskCompletionSource(); - var asyncTask = AwaitThenGetThreadAsync(unblockAsyncMethod.Task); + Task? asyncTask = AwaitThenGetThreadAsync(unblockAsyncMethod.Task); SynchronizationContext.SetSynchronizationContext(completeOnSyncContext); unblockAsyncMethod.SetResult(true); @@ -293,7 +291,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void ConfigureAwaitRunInline_AlreadyCompleted() { - var asyncTask = AwaitThenGetThreadAsync(Task.FromResult(true)); + Task? asyncTask = AwaitThenGetThreadAsync(Task.FromResult(true)); Assert.True(asyncTask.IsCompleted); Assert.Equal(Environment.CurrentManagedThreadId, asyncTask.Result); @@ -307,7 +305,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void ConfigureAwaitRunInlineOfT_AlreadyCompleted() { - var asyncTask = AwaitThenGetThreadAsync(Task.FromResult(true)); + Task? asyncTask = AwaitThenGetThreadAsync(Task.FromResult(true)); Assert.True(asyncTask.IsCompleted); Assert.Equal(Environment.CurrentManagedThreadId, asyncTask.Result); @@ -520,7 +518,7 @@ namespace Microsoft.VisualStudio.Threading.Tests Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); using (var test = new RegKeyTest()) { - using (var subKey = test.CreateSubKey()) + using (RegistryKey? subKey = test.CreateSubKey()) { Task changeWatcherTask = test.Key.WaitForChangeAsync(watchSubtree: true, cancellationToken: test.FinishedToken); subKey.SetValue("subkeyValueName", "b"); @@ -535,7 +533,7 @@ namespace Microsoft.VisualStudio.Threading.Tests Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); using (var test = new RegKeyTest()) { - using (var subKey = test.CreateSubKey()) + using (RegistryKey? subKey = test.CreateSubKey()) { Task changeWatcherTask = subKey.WaitForChangeAsync(watchSubtree: true, cancellationToken: test.FinishedToken); test.Key.DeleteSubKey(Path.GetFileName(subKey.Name)); @@ -550,7 +548,7 @@ namespace Microsoft.VisualStudio.Threading.Tests Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); using (var test = new RegKeyTest()) { - using (var subKey = test.CreateSubKey()) + using (RegistryKey? subKey = test.CreateSubKey()) { Task changeWatcherTask = test.Key.WaitForChangeAsync(watchSubtree: false, cancellationToken: test.FinishedToken); subKey.SetValue("subkeyValueName", "b"); @@ -716,6 +714,7 @@ namespace Microsoft.VisualStudio.Threading.Tests public void Dispose() { this.testFinished.Cancel(); + this.testFinished.Dispose(); this.key.Dispose(); Registry.CurrentUser.DeleteSubKeyTree(this.keyName); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs similarity index 69% rename from src/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs index 48a26a78..81aef0c7 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/CancellationTokenExtensionsTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Threading; @@ -15,7 +18,7 @@ [Fact] public void CombineWith_NoneCancelable() { - using (var combined = CancellationToken.None.CombineWith(CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(CancellationToken.None)) { Assert.False(combined.Token.CanBeCanceled); } @@ -25,7 +28,7 @@ public void CombineWith_FirstCancelable() { var cts = new CancellationTokenSource(); - using (var combined = cts.Token.CombineWith(CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts.Token.CombineWith(CancellationToken.None)) { Assert.True(combined.Token.CanBeCanceled); Assert.Equal(cts.Token, combined.Token); @@ -36,7 +39,7 @@ public void CombineWith_SecondCancelable() { var cts = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts.Token)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts.Token)) { Assert.True(combined.Token.CanBeCanceled); Assert.Equal(cts.Token, combined.Token); @@ -48,7 +51,7 @@ { var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); - using (var combined = cts1.Token.CombineWith(cts2.Token)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts1.Token.CombineWith(cts2.Token)) { Assert.True(combined.Token.CanBeCanceled); Assert.NotEqual(cts1.Token, combined.Token); @@ -64,7 +67,7 @@ { var first = new CancellationToken(true); var cts2 = new CancellationTokenSource(); - using (var combined = first.CombineWith(cts2.Token)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = first.CombineWith(cts2.Token)) { Assert.Equal(first, combined.Token); } @@ -75,7 +78,7 @@ { var cts1 = new CancellationTokenSource(); var second = new CancellationToken(true); - using (var combined = cts1.Token.CombineWith(second)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts1.Token.CombineWith(second)) { Assert.Equal(second, combined.Token); } @@ -85,7 +88,7 @@ public void CombineWith_Array_Empty() { var cts1 = new CancellationTokenSource(); - using (var combined = cts1.Token.CombineWith()) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts1.Token.CombineWith()) { Assert.Equal(cts1.Token, combined.Token); } @@ -101,7 +104,7 @@ [Fact] public void CombineWith_Array_Empty_OriginalNonCancelable() { - using (var combined = CancellationToken.None.CombineWith()) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith()) { Assert.False(combined.Token.CanBeCanceled); } @@ -111,7 +114,7 @@ public void CombineWith_Array_Empty_OriginalAlreadyCanceled() { CancellationToken cancellationToken = new CancellationToken(true); - using (var combined = cancellationToken.CombineWith()) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cancellationToken.CombineWith()) { Assert.True(combined.Token.IsCancellationRequested); Assert.Equal(cancellationToken, combined.Token); @@ -122,7 +125,7 @@ public void CombineWith_Array_OneArrayElementCancelable_First() { var cts1 = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None)) { Assert.Equal(cts1.Token, combined.Token); } @@ -132,7 +135,7 @@ public void CombineWith_Array_OneArrayElementCancelable_Second() { var cts1 = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None)) { Assert.Equal(cts1.Token, combined.Token); } @@ -142,7 +145,7 @@ public void CombineWith_Array_OneArrayElementPreCanceled() { var ct = new CancellationToken(true); - using (var combined = CancellationToken.None.CombineWith(CancellationToken.None, ct, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(CancellationToken.None, ct, CancellationToken.None)) { Assert.Equal(ct, combined.Token); Assert.True(combined.Token.IsCancellationRequested); @@ -154,7 +157,7 @@ { var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts1.Token, cts2.Token)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts1.Token, cts2.Token)) { Assert.True(combined.Token.CanBeCanceled); Assert.NotEqual(cts1.Token, combined.Token); @@ -169,7 +172,7 @@ { var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); - using (var combined = cts1.Token.CombineWith(cts2.Token, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts1.Token.CombineWith(cts2.Token, CancellationToken.None)) { Assert.NotEqual(cts1.Token, combined.Token); Assert.NotEqual(cts2.Token, combined.Token); @@ -184,7 +187,7 @@ { var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); - using (var combined = cts1.Token.CombineWith(CancellationToken.None, cts2.Token, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = cts1.Token.CombineWith(CancellationToken.None, cts2.Token, CancellationToken.None)) { Assert.NotEqual(cts1.Token, combined.Token); Assert.NotEqual(cts2.Token, combined.Token); @@ -207,7 +210,7 @@ { var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None, cts2.Token, CancellationToken.None)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None, cts2.Token, CancellationToken.None)) { Assert.NotEqual(cts1.Token, combined.Token); Assert.NotEqual(cts2.Token, combined.Token); @@ -230,7 +233,7 @@ var cts1 = new CancellationTokenSource(); var cts2 = new CancellationTokenSource(); var cts3 = new CancellationTokenSource(); - using (var combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None, cts2.Token, CancellationToken.None, cts3.Token)) + using (CancellationTokenExtensions.CombinedCancellationToken combined = CancellationToken.None.CombineWith(cts1.Token, CancellationToken.None, cts2.Token, CancellationToken.None, cts3.Token)) { Assert.NotEqual(cts1.Token, combined.Token); Assert.NotEqual(cts2.Token, combined.Token); @@ -244,8 +247,8 @@ [Fact] public void CombinedCancellationToken_Equality_BetweenEqualInstances_None() { - var combined1 = CancellationToken.None.CombineWith(CancellationToken.None); - var combined2 = CancellationToken.None.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined1 = CancellationToken.None.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined2 = CancellationToken.None.CombineWith(CancellationToken.None); Assert.Equal(combined1.GetHashCode(), combined2.GetHashCode()); Assert.True(combined1.Equals(combined2)); Assert.True(combined1 == combined2); @@ -256,8 +259,8 @@ public void CombinedCancellationToken_Equality_WithRealToken() { var cts = new CancellationTokenSource(); - var combined1 = cts.Token.CombineWith(CancellationToken.None); - var combined2 = cts.Token.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined1 = cts.Token.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined2 = cts.Token.CombineWith(CancellationToken.None); Assert.Equal(combined1.GetHashCode(), combined2.GetHashCode()); Assert.True(combined1.Equals(combined2)); Assert.True(combined1 == combined2); @@ -268,8 +271,8 @@ public void CombinedCancellationToken_Inequality_WithRealToken() { var cts = new CancellationTokenSource(); - var combined1 = cts.Token.CombineWith(CancellationToken.None); - var combined2 = CancellationToken.None.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined1 = cts.Token.CombineWith(CancellationToken.None); + CancellationTokenExtensions.CombinedCancellationToken combined2 = CancellationToken.None.CombineWith(CancellationToken.None); Assert.NotEqual(combined1.GetHashCode(), combined2.GetHashCode()); Assert.False(combined1.Equals(combined2)); Assert.False(combined1 == combined2); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs index a918f3c6..4781ddc5 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/DelegatingJoinableTaskFactoryTests.cs @@ -1,8 +1,5 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { @@ -51,7 +48,7 @@ namespace Microsoft.VisualStudio.Threading.Tests await Task.Delay(1); }); - var jt = delegatingFactory.RunAsync(async delegate + JoinableTask? jt = delegatingFactory.RunAsync(async delegate { await TaskScheduler.Default; await delegatingFactory.SwitchToMainThreadAsync(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs similarity index 65% rename from src/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs index bd2a4ef8..86e18b9f 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/DispatcherExtensionsTests.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETFRAMEWORK @@ -33,13 +30,13 @@ namespace Microsoft.VisualStudio.Threading.Tests { this.SimulateUIThread(async delegate { - var idlePriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.ApplicationIdle); - var normalPriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.Normal); - var idleTask = idlePriorityJtf.RunAsync(async delegate + JoinableTaskFactory? idlePriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.ApplicationIdle); + JoinableTaskFactory? normalPriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.Normal); + JoinableTask? idleTask = idlePriorityJtf.RunAsync(async delegate { await Task.Yield(); }); - var normalTask = normalPriorityJtf.RunAsync(async delegate + JoinableTask? normalTask = normalPriorityJtf.RunAsync(async delegate { await Task.Yield(); Assert.False(idleTask.IsCompleted); @@ -53,11 +50,11 @@ namespace Microsoft.VisualStudio.Threading.Tests { this.SimulateUIThread(async delegate { - var idlePriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.ApplicationIdle); - var normalPriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.Normal); + JoinableTaskFactory? idlePriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.ApplicationIdle); + JoinableTaskFactory? normalPriorityJtf = this.asyncPump.WithPriority(Dispatcher.CurrentDispatcher, DispatcherPriority.Normal); JoinableTask? normalTask = null; var unblockNormalPriorityWork = new AsyncManualResetEvent(); - var idleTask = idlePriorityJtf.RunAsync(async delegate + JoinableTask? idleTask = idlePriorityJtf.RunAsync(async delegate { await Task.Yield(); unblockNormalPriorityWork.Set(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs b/test/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs similarity index 69% rename from src/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs rename to test/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs index fcf8e6d4..0e1d5cb3 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/GenericParameterHelper.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { diff --git a/src/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs similarity index 77% rename from src/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs index 1b175be5..05c6def5 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/InternalUtilitiesTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -29,7 +32,7 @@ [Fact] public void RemoveMidQueue() { - var list = Enumerable.Range(1, 3).Select(i => new GenericParameterHelper(i)).ToArray(); + GenericParameterHelper[]? list = Enumerable.Range(1, 3).Select(i => new GenericParameterHelper(i)).ToArray(); for (int positionToRemove = 0; positionToRemove < list.Length; positionToRemove++) { var queue = new Queue(); @@ -48,14 +51,15 @@ GenericParameterHelper? lastDequeued = null; do { - var item = queue.Dequeue(); - if (lastDequeued != null) + GenericParameterHelper? item = queue.Dequeue(); + if (lastDequeued is object) { Assert.True(lastDequeued.Data < item.Data); } lastDequeued = item; - } while (queue.Count > 0); + } + while (queue.Count > 0); } } } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs index fa56d83c..862908e8 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskAndAsyncReaderWriterLockTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -100,13 +103,13 @@ { using (TestUtilities.DisableAssertionDialog()) { - using (var releaser1 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser1 = await this.asyncLock.WriteLockAsync()) { Assert.Throws(delegate { this.asyncPump.Run(async delegate { - using (var releaser2 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser2 = await this.asyncLock.WriteLockAsync()) { } }); @@ -120,14 +123,14 @@ { using (TestUtilities.DisableAssertionDialog()) { - using (var releaser1 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser1 = await this.asyncLock.WriteLockAsync()) { await Task.Yield(); Assert.Throws(delegate { this.asyncPump.Run(async delegate { - using (var releaser2 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser2 = await this.asyncLock.WriteLockAsync()) { await Task.Yield(); } @@ -213,7 +216,7 @@ async delegate { InitializeJoinableTaskFactory(out this.joinableCollection, out this.asyncPump); - using (var releaser1 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser1 = await this.asyncLock.WriteLockAsync()) { // This part of the scenario is where we switch back to the main thread // in preparation to call 3rd party code. @@ -226,7 +229,7 @@ // into code that may require the same write lock, via a synchronous interface. this.asyncPump.Run(async delegate { - using (var releaser2 = await this.asyncLock.WriteLockAsync()) + using (AsyncReaderWriterLock.Releaser releaser2 = await this.asyncLock.WriteLockAsync()) { await Task.Yield(); } @@ -244,7 +247,7 @@ private void LockWithinRunAsyncAfterYieldHelper() { - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); await this.VerifyReadLockAsync(); @@ -266,8 +269,8 @@ /// private async Task VerifyReadLockAsync() { - var lockRequest = this.asyncLock.ReadLockAsync(); - var lockRequestAwaiter = lockRequest.GetAwaiter(); + AsyncReaderWriterLock.Awaitable lockRequest = this.asyncLock.ReadLockAsync(); + AsyncReaderWriterLock.Awaiter? lockRequestAwaiter = lockRequest.GetAwaiter(); if (!lockRequestAwaiter.IsCompleted) { await lockRequestAwaiter.YieldAndNotify(this.lockRequested); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs similarity index 78% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs index a3dc8a2d..a65989d1 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskCollectionTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -38,7 +41,7 @@ [Fact] public void JoinTillEmptyAlreadyCompleted() { - var awaiter = this.joinableCollection!.JoinTillEmptyAsync().GetAwaiter(); + System.Runtime.CompilerServices.TaskAwaiter awaiter = this.joinableCollection!.JoinTillEmptyAsync().GetAwaiter(); Assert.True(awaiter.IsCompleted); } @@ -46,12 +49,12 @@ public void JoinTillEmptyWithOne() { var evt = new AsyncManualResetEvent(); - var joinable = this.JoinableFactory.RunAsync(async delegate + JoinableTask? joinable = this.JoinableFactory.RunAsync(async delegate { await evt; }); - var waiter = this.joinableCollection!.JoinTillEmptyAsync(); + Task? waiter = this.joinableCollection!.JoinTillEmptyAsync(); Assert.False(waiter.GetAwaiter().IsCompleted); Task.Run(async delegate { @@ -66,12 +69,12 @@ public void JoinTillEmptyUsesConfigureAwaitFalse() { var evt = new AsyncManualResetEvent(); - var joinable = this.JoinableFactory.RunAsync(async delegate + JoinableTask? joinable = this.JoinableFactory.RunAsync(async delegate { await evt.WaitAsync().ConfigureAwait(false); }); - var waiter = this.joinableCollection!.JoinTillEmptyAsync(); + Task? waiter = this.joinableCollection!.JoinTillEmptyAsync(); Assert.False(waiter.GetAwaiter().IsCompleted); evt.Set(); Assert.True(waiter.Wait(UnexpectedTimeout)); @@ -81,12 +84,12 @@ public void EmptyThenMore() { var evt = new AsyncManualResetEvent(); - var joinable = this.JoinableFactory.RunAsync(async delegate + JoinableTask? joinable = this.JoinableFactory.RunAsync(async delegate { await evt; }); - var waiter = this.joinableCollection!.JoinTillEmptyAsync(); + Task? waiter = this.joinableCollection!.JoinTillEmptyAsync(); Assert.False(waiter.GetAwaiter().IsCompleted); Task.Run(async delegate { @@ -100,7 +103,7 @@ [Fact] public void JoinTillEmptyAsyncJoinsCollection() { - var joinable = this.JoinableFactory.RunAsync(async delegate + JoinableTask? joinable = this.JoinableFactory.RunAsync(async delegate { await Task.Yield(); }); @@ -115,7 +118,7 @@ public void JoinTillEmptyAsync_CancellationToken() { var tcs = new TaskCompletionSource(); - var joinable = this.JoinableFactory.RunAsync(async delegate + JoinableTask? joinable = this.JoinableFactory.RunAsync(async delegate { await tcs.Task; }); @@ -125,7 +128,7 @@ var cts = new CancellationTokenSource(); Task joinTask = this.joinableCollection!.JoinTillEmptyAsync(cts.Token); cts.Cancel(); - var ex = await Assert.ThrowsAnyAsync(() => joinTask); + OperationCanceledException? ex = await Assert.ThrowsAnyAsync(() => joinTask); Assert.Equal(cts.Token, ex.CancellationToken); }); } @@ -134,7 +137,7 @@ public void AddTwiceRemoveOnceRemovesWhenNotRefCounting() { var finishTaskEvent = new AsyncManualResetEvent(); - var task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); + JoinableTask? task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); var collection = new JoinableTaskCollection(this.context, refCountAddedJobs: false); collection.Add(task); @@ -151,7 +154,7 @@ public void AddTwiceRemoveTwiceRemovesWhenRefCounting() { var finishTaskEvent = new AsyncManualResetEvent(); - var task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); + JoinableTask? task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); var collection = new JoinableTaskCollection(this.context, refCountAddedJobs: true); collection.Add(task); @@ -170,7 +173,7 @@ public void AddTwiceRemoveOnceRemovesCompletedTaskWhenRefCounting() { var finishTaskEvent = new AsyncManualResetEvent(); - var task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); + JoinableTask? task = this.JoinableFactory.RunAsync(async delegate { await finishTaskEvent; }); var collection = new JoinableTaskCollection(this.context, refCountAddedJobs: true); collection.Add(task); @@ -190,7 +193,7 @@ { this.JoinableFactory.Run(delegate { - var releaser = this.joinableCollection!.Join(); + JoinableTaskCollection.JoinRelease releaser = this.joinableCollection!.Join(); releaser.Dispose(); releaser.Dispose(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs similarity index 88% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs index 34ef8326..c9928c29 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextNodeTests.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { @@ -31,7 +28,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void CreateCollection() { - var collection = this.defaultNode.CreateCollection(); + JoinableTaskCollection? collection = this.defaultNode.CreateCollection(); Assert.NotNull(collection); collection = this.derivedNode.CreateCollection(); @@ -41,7 +38,7 @@ namespace Microsoft.VisualStudio.Threading.Tests [Fact] public void CreateFactory() { - var factory = this.defaultNode.CreateFactory(this.joinableCollection!); + JoinableTaskFactory? factory = this.defaultNode.CreateFactory(this.joinableCollection!); Assert.IsType(factory); factory = this.derivedNode.CreateFactory(this.joinableCollection!); @@ -100,8 +97,8 @@ namespace Microsoft.VisualStudio.Threading.Tests factory.Run(async delegate { var timeout = Task.Delay(AsyncDelay); - var result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); - Assert.NotSame(timeout, result); //, "Timed out waiting for hang detection."); + Task? result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); + Assert.NotSame(timeout, result); // Timed out waiting for hang detection. }); Assert.True(this.derivedNode.HangDetected.IsSet); Assert.True(this.derivedNode.FalseHangReportDetected.IsSet); @@ -128,7 +125,7 @@ namespace Microsoft.VisualStudio.Threading.Tests factory.HangDetectionTimeout = TimeSpan.FromMilliseconds(1); this.derivedNode.RegisterOnHangDetected(); - var dectionTask = factory.RunAsync(async delegate + JoinableTask? dectionTask = factory.RunAsync(async delegate { await TaskScheduler.Default.SwitchTo(alwaysYield: true); for (int i = 0; i < 2; i++) @@ -143,8 +140,8 @@ namespace Microsoft.VisualStudio.Threading.Tests factory.Run(async delegate { var timeout = Task.Delay(AsyncDelay); - var result = await Task.WhenAny(timeout, dectionTask.Task); - Assert.NotSame(timeout, result); //, "Timed out waiting for hang detection."); + Task? result = await Task.WhenAny(timeout, dectionTask.Task); + Assert.NotSame(timeout, result); // Timed out waiting for hang detection. await dectionTask; }); @@ -167,14 +164,14 @@ namespace Microsoft.VisualStudio.Threading.Tests factory.Run(async delegate { var timeout = Task.Delay(AsyncDelay); - var result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); - Assert.NotSame(timeout, result); //, "Timed out waiting for hang detection."); + Task? result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); + Assert.NotSame(timeout, result); // Timed out waiting for hang detection. }); Assert.True(this.derivedNode.HangDetected.IsSet); Assert.NotNull(this.derivedNode.HangDetails); Assert.NotNull(this.derivedNode.HangDetails.EntryMethod); Assert.Same(this.GetType(), this.derivedNode.HangDetails.EntryMethod!.DeclaringType); - Assert.Contains(nameof(this.OnHangDetected_Run_OnMainThread), this.derivedNode.HangDetails.EntryMethod.Name); + Assert.Contains(nameof(this.OnHangDetected_Run_OnMainThread), this.derivedNode.HangDetails.EntryMethod.Name, StringComparison.Ordinal); Assert.True(this.derivedNode.FalseHangReportDetected.IsSet); Assert.NotEqual(Guid.Empty, this.derivedNode.FalseHangReportId); @@ -199,21 +196,21 @@ namespace Microsoft.VisualStudio.Threading.Tests factory.HangDetectionTimeout = TimeSpan.FromMilliseconds(1); this.derivedNode.RegisterOnHangDetected(); - var jt = factory.RunAsync(async delegate + JoinableTask? jt = factory.RunAsync(async delegate { var timeout = Task.Delay(UnexpectedTimeout); - var result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); - Assert.NotSame(timeout, result); //, "Timed out waiting for hang detection."); + Task? result = await Task.WhenAny(timeout, this.derivedNode.HangDetected.WaitAsync()); + Assert.NotSame(timeout, result); // Timed out waiting for hang detection. }); OnHangDetected_BlockingMethodHelper(jt); Assert.True(this.derivedNode.HangDetected.IsSet); - var hangDetails = this.derivedNode.FirstHangDetails; + JoinableTaskContext.HangDetails? hangDetails = this.derivedNode.FirstHangDetails; Assert.NotNull(hangDetails); Assert.NotNull(hangDetails.EntryMethod); // Verify that the original method that spawned the JoinableTask is the one identified as the entrypoint method. Assert.Same(this.GetType(), hangDetails.EntryMethod!.DeclaringType); - Assert.Contains(nameof(this.OnHangDetected_RunAsync_OnMainThread_BlamedMethodIsEntrypointNotBlockingMethod), hangDetails.EntryMethod.Name); + Assert.Contains(nameof(this.OnHangDetected_RunAsync_OnMainThread_BlamedMethodIsEntrypointNotBlockingMethod), hangDetails.EntryMethod.Name, StringComparison.Ordinal); } [Fact] diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs index e52bdf1d..7751ff62 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -58,7 +61,7 @@ Task.Run(async delegate { - var ct = new CancellationTokenSource(TestTimeout).Token; + CancellationToken ct = new CancellationTokenSource(TestTimeout).Token; try { TimeSpan lastDuration = TimeSpan.Zero; @@ -66,10 +69,10 @@ Guid lastId = Guid.Empty; for (int i = 0; i < 3; i++) { - var tuple = await hangQueue.DequeueAsync(ct); - var duration = tuple.Item1; + Tuple? tuple = await hangQueue.DequeueAsync(ct); + TimeSpan duration = tuple.Item1; var iterations = tuple.Item2; - var id = tuple.Item3; + Guid id = tuple.Item3; Assert.True(lastDuration == TimeSpan.Zero || lastDuration < duration); Assert.Equal(lastIteration + 1, iterations); Assert.NotEqual(Guid.Empty, id); @@ -100,7 +103,7 @@ bool hangReported = false; this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported = true; - var joinableTask = this.Factory.RunAsync( + JoinableTask? joinableTask = this.Factory.RunAsync( () => Task.Delay((int)this.Factory.HangDetectionTimeout.TotalMilliseconds * 3)); joinableTask.Task.Wait(); // don't Join, since we're trying to simulate RunAsync not becoming synchronous. @@ -120,13 +123,13 @@ Task.Run(async delegate { - var ct = new CancellationTokenSource(TestTimeout).Token; + CancellationToken ct = new CancellationTokenSource(TestTimeout).Token; try { TimeSpan lastDuration = TimeSpan.Zero; for (int i = 0; i < 3; i++) { - var duration = await hangQueue.DequeueAsync(ct); + TimeSpan duration = await hangQueue.DequeueAsync(ct); Assert.True(lastDuration == TimeSpan.Zero || lastDuration < duration); lastDuration = duration; } @@ -139,7 +142,7 @@ } }).Forget(); - var joinableTask = this.Factory.RunAsync(async delegate + JoinableTask? joinableTask = this.Factory.RunAsync(async delegate { await releaseTaskSource.Task; }); @@ -173,7 +176,7 @@ this.Factory.Run( async () => { - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(20); @@ -193,7 +196,7 @@ bool hangReported = false; this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported = true; - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(30); @@ -216,7 +219,7 @@ bool hangReported = false; this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported = true; - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(30); @@ -236,7 +239,7 @@ var releaseUnrelatedTask = new AsyncManualResetEvent(); this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported.Set(); - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await releaseUnrelatedTask; @@ -266,7 +269,7 @@ var releaseUnrelatedTask = new AsyncManualResetEvent(); this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported.Set(); - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await releaseUnrelatedTask; @@ -281,7 +284,7 @@ this.Factory.Run( async () => { - using (var tempJoin = taskCollection.Join()) + using (JoinableTaskCollection.JoinRelease tempJoin = taskCollection.Join()) { await Task.Yield(); } @@ -303,7 +306,7 @@ var hangReported = new AsyncManualResetEvent(); this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported.Set(); - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(40); @@ -314,7 +317,7 @@ async () => { var cancellationSource = new CancellationTokenSource(); - var joinTask = task.JoinAsync(cancellationSource.Token); + Task? joinTask = task.JoinAsync(cancellationSource.Token); cancellationSource.Cancel(); await joinTask.NoThrowAwaitable(); @@ -331,7 +334,7 @@ var hangReported = new AsyncManualResetEvent(); this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported.Set(); - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(30); @@ -347,7 +350,7 @@ this.Factory.Run( async () => { - using (var tempJoin = taskCollection.Join()) + using (JoinableTaskCollection.JoinRelease tempJoin = taskCollection.Join()) { await hangReported.WaitAsync().WithTimeout(UnexpectedTimeout); } @@ -362,7 +365,7 @@ this.Context.OnReportHang = (hangDuration, iterations, id) => hangReported.Set(); var cancellationSource = new CancellationTokenSource(); - var task = this.Factory.RunAsync( + JoinableTask? task = this.Factory.RunAsync( async () => { await Task.Delay(40, cancellationSource.Token); @@ -375,7 +378,7 @@ this.Factory.Run( async () => { - using (var tempJoin = taskCollection.Join()) + using (JoinableTaskCollection.JoinRelease tempJoin = taskCollection.Join()) { cancellationSource.Cancel(); await task.JoinAsync().NoThrowAwaitable(); @@ -388,7 +391,7 @@ public void GetHangReportSimple() { IHangReportContributor contributor = this.Context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); Assert.Equal("application/xml", report.ContentType); Assert.NotNull(report.ContentName); this.Logger.WriteLine(report.Content); @@ -406,10 +409,10 @@ this.Factory.RunAsync(delegate { IHangReportContributor contributor = this.Context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); - var collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() + IEnumerable? collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() where node.Attribute(XName.Get("Category"))?.Value == "Collection" select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label == jtcName); @@ -423,16 +426,16 @@ var mainThreadRequested = new ManualResetEventSlim(); Task.Run(delegate { - var awaiter = this.Factory.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.Factory.SwitchToMainThreadAsync().GetAwaiter(); awaiter.OnCompleted(delegate { /* this anonymous delegate is expected to include the name of its containing method */ }); mainThreadRequested.Set(); }); mainThreadRequested.Wait(); IHangReportContributor contributor = this.Context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); - var collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() + IEnumerable? collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() where node.Attribute(XName.Get("Category"))?.Value == "Task" select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label.Contains(nameof(this.GetHangReportProducesDgmlWithMethodNameRequestingMainThread))); @@ -447,15 +450,15 @@ var nowait = Task.Run(async delegate { await this.Factory.SwitchToMainThreadAsync(); - var nowait2 = this.YieldingMethodAsync(); + Task? nowait2 = this.YieldingMethodAsync(); messagePosted.Set(); }); await messagePosted.WaitAsync(); IHangReportContributor contributor = this.Context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); - var collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() + IEnumerable? collectionLabels = from node in dgml.Root.Element(XName.Get("Nodes", DgmlNamespace)).Elements() where node.Attribute(XName.Get("Category"))?.Value == "Task" select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label.Contains(nameof(this.YieldingMethodAsync))); @@ -469,7 +472,7 @@ this.Context.OnReportHang = (hangDuration, iterations, id) => { IHangReportContributor contributor = this.Context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); this.Logger.WriteLine(report.Content); endTestTokenSource.Cancel(); this.Context.OnReportHang = null; @@ -503,7 +506,7 @@ [Fact] public void IsMainThreadBlockedFalseWhenAsync() { - var joinable = this.Factory.RunAsync(async delegate + JoinableTask? joinable = this.Factory.RunAsync(async delegate { Assert.False(this.Context.IsMainThreadBlocked()); await Task.Yield(); @@ -518,7 +521,7 @@ [Fact] public void IsMainThreadBlockedTrueWhenAsyncBecomesBlocking() { - var joinable = this.Factory.RunAsync(async delegate + JoinableTask? joinable = this.Factory.RunAsync(async delegate { Assert.False(this.Context.IsMainThreadBlocked()); await Task.Yield(); @@ -542,7 +545,7 @@ [Fact] public void IsMainThreadBlockedTrueWhenAsyncBecomesBlockingWithNestedTask() { - var joinable = this.Factory.RunAsync(async delegate + JoinableTask? joinable = this.Factory.RunAsync(async delegate { Assert.False(this.Context.IsMainThreadBlocked()); await Task.Yield(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs similarity index 94% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs index 96060177..ef25a270 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskFactoryTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -39,7 +42,7 @@ // It so happens as of the time of this writing that the Factory property // always requires a SyncContextLock. If it ever stops needing that, // we'll need to change this delegate to do something else that requires it. - var temp = this.context.Factory; + JoinableTaskFactory? temp = this.context.Factory; }); // Wait up to the timeout interval. Don't Assert here because @@ -48,7 +51,7 @@ // after we exit this critical section. noDeadlockDetected = otherThread.Wait(UnexpectedTimeout); }; - var jt = jtf.RunAsync(async delegate + JoinableTask? jt = jtf.RunAsync(async delegate { await jtf.SwitchToMainThreadAsync(); }); @@ -82,7 +85,7 @@ // It so happens as of the time of this writing that the Factory property // always requires a SyncContextLock. If it ever stops needing that, // we'll need to change this delegate to do something else that requires it. - var temp = this.context.Factory; + JoinableTaskFactory? temp = this.context.Factory; }); // Wait up to the timeout interval. Don't Assert here because diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs similarity index 76% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs index 4dd2d3bd..7c6d72e4 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTestBase.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -29,7 +32,9 @@ { this.dispatcherContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(this.dispatcherContext); +#pragma warning disable CA2214 // Do not call overridable methods in constructors this.context = this.CreateJoinableTaskContext(); +#pragma warning restore CA2214 // Do not call overridable methods in constructors this.joinableCollection = this.context.CreateCollection(); this.asyncPump = this.context.CreateFactory(this.joinableCollection); this.originalThreadManagedId = Environment.CurrentManagedThreadId; @@ -47,7 +52,7 @@ protected int GetPendingTasksCount() { IHangReportContributor hangContributor = this.context; - var contribution = hangContributor.GetHangReport(); + HangReportContribution? contribution = hangContributor.GetHangReport(); var dgml = XDocument.Parse(contribution.Content); return dgml.Descendants(XName.Get("Node", DgmlNamespace)).Count(n => n.Attributes("Category").Any(c => c.Value == "Task")); } @@ -57,24 +62,26 @@ Verify.Operation(this.originalThreadManagedId == Environment.CurrentManagedThreadId, "We can only simulate the UI thread if you're already on it (the starting thread for the test)."); Exception? failure = null; - this.dispatcherContext.Post(async delegate - { - try + this.dispatcherContext.Post( + async delegate { - await testMethod(); - } - catch (Exception ex) - { - failure = ex; - } - finally - { - this.testFrame.Continue = false; - } - }, null); + try + { + await testMethod(); + } + catch (Exception ex) + { + failure = ex; + } + finally + { + this.testFrame.Continue = false; + } + }, + null); SingleThreadedTestSynchronizationContext.PushFrame(this.dispatcherContext, this.testFrame); - if (failure != null) + if (failure is object) { // Rethrow original exception without rewriting the callstack. ExceptionDispatchInfo.Capture(failure).Throw(); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs index ded91034..e082c413 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Concurrent; @@ -85,7 +88,7 @@ Task.Run(delegate { - var awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); awaiter.OnCompleted(delegate { try @@ -116,7 +119,7 @@ Assert.Equal(0, this.GetPendingTasksCount()); Assert.Empty(this.joinableCollection); - if (delegateFailure != null) + if (delegateFailure is object) { throw new TargetInvocationException(delegateFailure); } @@ -170,7 +173,7 @@ this.asyncPump.RunAsync( delegate { - var awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); awaiter.OnCompleted( delegate { @@ -225,7 +228,7 @@ Assert.Equal(0, ((DerivedJoinableTaskFactory)this.asyncPump).TransitioningTasksCount); - if (delegateFailure != null) + if (delegateFailure is object) { throw new TargetInvocationException(delegateFailure); } @@ -253,7 +256,7 @@ Task.Run(delegate { asyncLocal.Value = asyncLocalValue; - var awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); awaiter.OnCompleted(delegate { try @@ -278,7 +281,7 @@ // Now let the request proceed through. this.PushFrame(); - if (delegateFailure != null) + if (delegateFailure is object) { throw new TargetInvocationException(delegateFailure); } @@ -479,7 +482,7 @@ { this.SimulateUIThread(async delegate { - var awaiter = this.asyncPump.SwitchToMainThreadAsync(new CancellationToken(true)).GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync(new CancellationToken(true)).GetAwaiter(); Assert.True(awaiter.IsCompleted); Assert.Throws(() => awaiter.GetResult()); @@ -494,7 +497,7 @@ { this.SimulateUIThread(async delegate { - var awaiter = this.asyncPump.SwitchToMainThreadAsync(alwaysYield: true, new CancellationToken(true)).GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync(alwaysYield: true, new CancellationToken(true)).GetAwaiter(); Assert.False(awaiter.IsCompleted); var testResult = new TaskCompletionSource(); awaiter.OnCompleted(delegate @@ -522,9 +525,9 @@ return Task.Run(delegate { var precanceled = new CancellationToken(canceled: true); - var awaiter = this.asyncPump.SwitchToMainThreadAsync(precanceled).GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync(precanceled).GetAwaiter(); Assert.True(awaiter.IsCompleted); - var ex = Assert.Throws(() => awaiter.GetResult()); + OperationCanceledException? ex = Assert.Throws(() => awaiter.GetResult()); Assert.Equal(precanceled, ex.CancellationToken); Assert.Null(SynchronizationContext.Current); }); @@ -541,10 +544,10 @@ this.asyncPump.Run(async delegate { // We're on a background thread, so a SyncContext applies even if it isn't the main thread one. - var bkgrndSyncContext = SynchronizationContext.Current; + SynchronizationContext? bkgrndSyncContext = SynchronizationContext.Current; Assert.NotNull(bkgrndSyncContext); var cts = new CancellationTokenSource(); - var awaiter = this.asyncPump.SwitchToMainThreadAsync(cts.Token).GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync(cts.Token).GetAwaiter(); Assert.False(awaiter.IsCompleted); var tcs = new TaskCompletionSource(); @@ -581,7 +584,7 @@ return Task.Run(async delegate { var cts = new CancellationTokenSource(); - var awaiter = this.asyncPump.SwitchToMainThreadAsync(cts.Token).GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync(cts.Token).GetAwaiter(); Assert.False(awaiter.IsCompleted); var testResult = new TaskCompletionSource(); awaiter.OnCompleted(delegate @@ -590,7 +593,7 @@ { Assert.True(this.context.IsOnMainThread); cts.Cancel(); - var ex = Assert.Throws(() => awaiter.GetResult()); + OperationCanceledException? ex = Assert.Throws(() => awaiter.GetResult()); Assert.Equal(cts.Token, ex.CancellationToken); testResult.SetResult(null); } @@ -618,7 +621,7 @@ Task.Run(async delegate { // Create the JoinableTaskContext on a dedicated thread which no SynchronizationContext can ever switch back to. - var jtc = await Task.Factory.StartNew(() => new JoinableTaskContext(Thread.CurrentThread, new SynchronizationContext()), this.TimeoutToken, TaskCreationOptions.LongRunning | TaskCreationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); + JoinableTaskContext? jtc = await Task.Factory.StartNew(() => new JoinableTaskContext(Thread.CurrentThread, new SynchronizationContext()), this.TimeoutToken, TaskCreationOptions.LongRunning | TaskCreationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); // Now ask the JTC to switch to that main thread. It should throw when it fails to do so. await Assert.ThrowsAsync(async () => await jtc.Factory.SwitchToMainThreadAsync(this.TimeoutToken)); @@ -737,7 +740,7 @@ } // stop inviting more work from background thread. await this.asyncPump.SwitchToMainThreadAsync(); - var nowait = backgroundInvitationReverted.SetAsync(); + Task? nowait = backgroundInvitationReverted.SetAsync(); Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); syncUIOperationCompleted = true; @@ -759,27 +762,27 @@ { // Switch to main thread when we're already there. await factory.SwitchToMainThreadAsync(); - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected since we're already on the main thread."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected since we're already on the main thread."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected since we're already on the main thread. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected since we're already on the main thread. // While on the main thread, await something that executes on a background thread. await Task.Run(delegate { - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected when moving off the main thread."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected when moving off the main thread."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected when moving off the main thread. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected when moving off the main thread. }); - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected since the main thread was ultimately blocked for this job."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected since the main thread was ultimately blocked for this job."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected since the main thread was ultimately blocked for this job. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected since the main thread was ultimately blocked for this job. // Now switch explicitly to a threadpool thread. await TaskScheduler.Default.SwitchTo(alwaysYield: true); - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected when moving off the main thread."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected when moving off the main thread."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected when moving off the main thread. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected when moving off the main thread. // Now switch back to the main thread. await factory.SwitchToMainThreadAsync(); - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected because the main thread was ultimately blocked for this job."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected because the main thread was ultimately blocked for this job."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected because the main thread was ultimately blocked for this job. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected because the main thread was ultimately blocked for this job. }); } @@ -788,23 +791,23 @@ { var factory = (DerivedJoinableTaskFactory)this.asyncPump; - var joinableTask = factory.RunAsync(async delegate + JoinableTask? joinableTask = factory.RunAsync(async delegate { // Switch to main thread when we're already there. await factory.SwitchToMainThreadAsync(); - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected since we're already on the main thread."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected since we're already on the main thread."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected since we're already on the main thread. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected since we're already on the main thread. // While on the main thread, await something that executes on a background thread. var task = Task.Run(delegate { - Assert.Equal(0, factory.TransitioningToMainThreadHitCount); //, "No transition expected when moving off the main thread."); - Assert.Equal(0, factory.TransitionedToMainThreadHitCount); //, "No transition expected when moving off the main thread."); + Assert.Equal(0, factory.TransitioningToMainThreadHitCount); // No transition expected when moving off the main thread. + Assert.Equal(0, factory.TransitionedToMainThreadHitCount); // No transition expected when moving off the main thread. }); await task.GetAwaiter().YieldAndNotify(); // ensure we yield for this task. await task; // rethrow any exceptions. - Assert.Equal(1, factory.TransitioningToMainThreadHitCount); //, "Reacquisition of main thread should have raised transition events."); - Assert.Equal(1, factory.TransitionedToMainThreadHitCount); //, "Reacquisition of main thread should have raised transition events."); + Assert.Equal(1, factory.TransitioningToMainThreadHitCount); // Reacquisition of main thread should have raised transition events. + Assert.Equal(1, factory.TransitionedToMainThreadHitCount); // Reacquisition of main thread should have raised transition events. // Now switch explicitly to a threadpool thread. await TaskScheduler.Default.SwitchTo(alwaysYield: true); @@ -926,7 +929,7 @@ this.asyncPump.Run(async delegate { await this.asyncPump.SwitchToMainThreadAsync(); - Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); //, "We're not on the Main thread!"); + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); // We're not on the Main thread! }); }); @@ -935,7 +938,7 @@ // Even though it's all the same instance of AsyncPump, // unrelated work (work not spun off from this block) must still be // Joined in order to execute here. - Assert.NotSame(task, await Task.WhenAny(task, Task.Delay(AsyncDelay / 2))); //, "The unrelated main thread work completed before the Main thread was joined."); + Assert.NotSame(task, await Task.WhenAny(task, Task.Delay(AsyncDelay / 2))); // The unrelated main thread work completed before the Main thread was joined. using (this.joinableCollection!.Join()) { this.PrintActiveTasksReport(); @@ -947,8 +950,8 @@ [Fact] public void RunSynchronouslyOffMainThreadRequiresJoinToReenterMainThreadForDifferentAsyncPumpInstance() { - var otherCollection = this.context.CreateCollection(); - var otherAsyncPump = this.context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.context.CreateCollection(); + JoinableTaskFactory? otherAsyncPump = this.context.CreateFactory(otherCollection); var task = Task.Run(delegate { otherAsyncPump.Run(async delegate @@ -960,7 +963,7 @@ this.asyncPump.Run(async delegate { - Assert.NotSame(task, await Task.WhenAny(task, Task.Delay(AsyncDelay / 2))); //, "The unrelated main thread work completed before the Main thread was joined."); + Assert.NotSame(task, await Task.WhenAny(task, Task.Delay(AsyncDelay / 2))); // The unrelated main thread work completed before the Main thread was joined. using (otherCollection.Join()) { await task; @@ -1682,15 +1685,15 @@ // STEP 5 joinReverted.Set(); - var releasingTask = await Task.WhenAny(unrelatedTask, postJoinRevertedWorkQueued.WaitAsync()); + Task? releasingTask = await Task.WhenAny(unrelatedTask, postJoinRevertedWorkQueued.WaitAsync()); if (releasingTask == unrelatedTask && unrelatedTask.IsFaulted) { unrelatedTask.GetAwaiter().GetResult(); // rethrow an error that has already occurred. } // STEP 7 - var executingWaitTask = postJoinRevertedWorkExecuting.WaitAsync(); - Assert.NotSame(executingWaitTask, await Task.WhenAny(executingWaitTask, Task.Delay(AsyncDelay))); //, "Main thread work from unrelated task should not have executed."); + Task? executingWaitTask = postJoinRevertedWorkExecuting.WaitAsync(); + Assert.NotSame(executingWaitTask, await Task.WhenAny(executingWaitTask, Task.Delay(AsyncDelay))); // Main thread work from unrelated task should not have executed. await Task.Yield(); Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); @@ -1713,7 +1716,7 @@ [Fact] public void SyncContextRestoredAfterRun() { - var syncContext = SynchronizationContext.Current; + SynchronizationContext? syncContext = SynchronizationContext.Current; Assert.NotNull(syncContext); // We need a non-null sync context for this test to be useful. this.asyncPump.Run(async delegate @@ -1798,7 +1801,7 @@ outerService = new MockAsyncService(this.asyncPump.Context, outerService); } - var operationTask = outerService!.OperationAsync(); + Task? operationTask = outerService!.OperationAsync(); this.asyncPump.Run(async delegate { @@ -1906,7 +1909,7 @@ [Fact] public void KickOffAsyncWorkFromMainThreadThenBlockOnIt() { - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await this.SomeOperationThatMayBeOnMainThreadAsync(); }); @@ -1923,7 +1926,7 @@ [Fact] public void KickOffDeepAsyncWorkFromMainThreadThenBlockOnIt() { - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await this.SomeOperationThatUsesMainThreadViaItsOwnAsyncPumpAsync(); }); @@ -1981,7 +1984,7 @@ [Fact] public void BeginAsyncYieldsToAppropriateContext() { - var backgroundWork = Task.Run(delegate + Task? backgroundWork = Task.Run(delegate { return this.asyncPump.RunAsync(async delegate { @@ -2030,19 +2033,19 @@ [Fact] public void BeginAsyncOnMTAKicksOffOtherAsyncPumpWorkCanCompleteSynchronouslySwitchFirst() { - var otherCollection = this.asyncPump.Context.CreateCollection(); - var otherPump = this.asyncPump.Context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.asyncPump.Context.CreateCollection(); + JoinableTaskFactory? otherPump = this.asyncPump.Context.CreateFactory(otherCollection); bool taskFinished = false; var switchPended = new ManualResetEventSlim(); // Kick off the BeginAsync work from a background thread that has no special // affinity to the main thread. - var joinable = Task.Run(delegate + JoinableTask? joinable = Task.Run(delegate { return this.asyncPump.RunAsync(async delegate { await Task.Yield(); - var awaiter = otherPump.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = otherPump.SwitchToMainThreadAsync().GetAwaiter(); Assert.False(awaiter.IsCompleted); var continuationFinished = new AsyncManualResetEvent(); awaiter.OnCompleted(delegate @@ -2065,14 +2068,14 @@ [Fact] public void BeginAsyncOnMTAKicksOffOtherAsyncPumpWorkCanCompleteSynchronouslyJoinFirst() { - var otherCollection = this.asyncPump.Context.CreateCollection(); - var otherPump = this.asyncPump.Context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.asyncPump.Context.CreateCollection(); + JoinableTaskFactory? otherPump = this.asyncPump.Context.CreateFactory(otherCollection); bool taskFinished = false; var joinedEvent = new AsyncManualResetEvent(); // Kick off the BeginAsync work from a background thread that has no special // affinity to the main thread. - var joinable = Task.Run(delegate + JoinableTask? joinable = Task.Run(delegate { return this.asyncPump.RunAsync(async delegate { @@ -2085,7 +2088,7 @@ Assert.False(joinable.Task.IsCompleted); this.asyncPump.Run(async delegate { - var awaitable = joinable.JoinAsync(); + Task? awaitable = joinable.JoinAsync(); joinedEvent.Set(); await awaitable; }); @@ -2096,13 +2099,13 @@ [Fact] public void BeginAsyncWithResultOnMTAKicksOffOtherAsyncPumpWorkCanCompleteSynchronously() { - var otherCollection = this.asyncPump.Context.CreateCollection(); - var otherPump = this.asyncPump.Context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.asyncPump.Context.CreateCollection(); + JoinableTaskFactory? otherPump = this.asyncPump.Context.CreateFactory(otherCollection); bool taskFinished = false; // Kick off the BeginAsync work from a background thread that has no special // affinity to the main thread. - var joinable = Task.Run(delegate + JoinableTask? joinable = Task.Run(delegate { return this.asyncPump.RunAsync(async delegate { @@ -2125,7 +2128,7 @@ { // Kick off the BeginAsync work from a background thread that has no special // affinity to the main thread. - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); await this.asyncPump.SwitchToMainThreadAsync(); @@ -2161,7 +2164,14 @@ syncContext = SynchronizationContext.Current; bool executed1 = false; - syncContext!.Send(s => { Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); Assert.Same(state, s); executed1 = true; }, state); + syncContext!.Send( + s => + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed1 = true; + }, + state); Assert.True(executed1); // And from another thread. But the Main thread is "busy" in a synchronous block, @@ -2171,20 +2181,22 @@ sendFromWithinRunSync = Task.Run(delegate { bool executed2 = false; - syncContext.Send(s => - { - try - { - Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); - Assert.Same(state, s); - executed2 = true; - } - finally - { - // Allow the message pump to exit. - countdownEvent.Signal(); - } - }, state); + syncContext.Send( + s => + { + try + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed2 = true; + } + finally + { + // Allow the message pump to exit. + countdownEvent.Signal(); + } + }, + state); Assert.True(executed2); }); @@ -2193,7 +2205,14 @@ // From the Main thread. bool executed3 = false; - syncContext!.Send(s => { Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); Assert.Same(state, s); executed3 = true; }, state); + syncContext!.Send( + s => + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed3 = true; + }, + state); Assert.True(executed3); // And from another thread. @@ -2202,12 +2221,14 @@ try { bool executed4 = false; - syncContext.Send(s => - { - Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); - Assert.Same(state, s); - executed4 = true; - }, state); + syncContext.Send( + s => + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed4 = true; + }, + state); Assert.True(executed4); } finally @@ -2240,13 +2261,27 @@ syncContext = SynchronizationContext.Current; bool executed1 = false; - syncContext!.Send(s => { Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); Assert.Same(state, s); executed1 = true; }, state); + syncContext!.Send( + s => + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed1 = true; + }, + state); Assert.True(executed1); await TaskScheduler.Default.SwitchTo(alwaysYield: true); bool executed2 = false; - syncContext.Send(s => { Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); Assert.Same(state, s); executed2 = true; }, state); + syncContext.Send( + s => + { + Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); + Assert.Same(state, s); + executed2 = true; + }, + state); Assert.True(executed2); } finally @@ -2272,7 +2307,7 @@ { this.asyncPump.Run(delegate { - var syncContext = SynchronizationContext.Current; // simulate someone who has captured our own sync context. + SynchronizationContext? syncContext = SynchronizationContext.Current; // simulate someone who has captured our own sync context. Exception? ex = null; using (this.context.SuppressRelevance()) { // simulate some kind of sync context hand-off that doesn't flow execution context. @@ -2303,7 +2338,7 @@ // This will hang unless the message gets processed. this.PushFrame(); - if (ex != null) + if (ex is object) { Assert.True(false, $"Posted message threw an exception: {ex}"); } @@ -2317,8 +2352,8 @@ { Task? backgroundTask = null; var mainThreadUnblocked = new AsyncManualResetEvent(); - var otherCollection = this.context.CreateCollection(); - var otherPump = this.context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.context.CreateCollection(); + JoinableTaskFactory? otherPump = this.context.CreateFactory(otherCollection); otherPump.Run(delegate { this.asyncPump.Run(delegate @@ -2405,7 +2440,7 @@ public void JoinWorkStealingRetainsThreadAffinityUI() { bool synchronousCompletionStarting = false; - var asyncTask = this.asyncPump.RunAsync(async delegate + Task? asyncTask = this.asyncPump.RunAsync(async delegate { int iterationsRemaining = 20; while (iterationsRemaining > 0) @@ -2474,7 +2509,7 @@ [Fact] public void BeginAsyncThenJoinOnMainThread() { - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); await Task.Yield(); @@ -2501,7 +2536,7 @@ var startingJoin = new AsyncManualResetEvent(); ((DerivedJoinableTaskFactory)this.asyncPump).AssumeConcurrentUse = true; - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); firstYield.Set(); @@ -2549,8 +2584,8 @@ // The repro in VS wasn't as concise (or possibly as contrived looking) as this. // This code sets up the minimal scenario for reproducing the bug that came about // through interactions of various CPS/VC components. - var otherCollection = this.context.CreateCollection(); - var otherPump = this.context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.context.CreateCollection(); + JoinableTaskFactory? otherPump = this.context.CreateFactory(otherCollection); otherPump.Run(async delegate { await this.asyncPump.RunAsync(delegate @@ -2558,7 +2593,7 @@ return Task.Run(async delegate { await messagePosted; // wait for this.asyncPump.pendingActions to be non empty - using (var j = this.joinableCollection!.Join()) + using (JoinableTaskCollection.JoinRelease j = this.joinableCollection!.Join()) { await uiThreadReachedTask; } @@ -2570,23 +2605,25 @@ [Fact] public void NoPostedMessageLost() { - Assert.True(Task.Run(async delegate - { - var delegateExecuted = new AsyncManualResetEvent(); - SynchronizationContext? syncContext = null; - this.asyncPump.Run(delegate - { - syncContext = SynchronizationContext.Current; - return Task.CompletedTask; - }); - syncContext!.Post( - delegate + Assert.True( + Task.Run( + async delegate { - delegateExecuted.Set(); - }, - null); - await delegateExecuted; - }).Wait(TestTimeout), "Timed out waiting for completion."); + var delegateExecuted = new AsyncManualResetEvent(); + SynchronizationContext? syncContext = null; + this.asyncPump.Run(delegate + { + syncContext = SynchronizationContext.Current; + return Task.CompletedTask; + }); + syncContext!.Post( + delegate + { + delegateExecuted.Set(); + }, + null); + await delegateExecuted; + }).Wait(TestTimeout), "Timed out waiting for completion."); } [Fact] @@ -2611,7 +2648,7 @@ var loPriFactory = new ModalPumpingJoinableTaskFactory(this.context); var hiPriFactory = new ModalPumpingJoinableTaskFactory(this.context); - var outer = hiPriFactory.RunAsync(async delegate + JoinableTask? outer = hiPriFactory.RunAsync(async delegate { await loPriFactory.RunAsync(async delegate { @@ -2648,7 +2685,7 @@ var outerFinished = new AsyncManualResetEvent(allowInliningAwaiters: true); JoinableTask innerTask; AsyncManualResetEvent loPriSwitchPosted = new AsyncManualResetEvent(); - var outer = hiPriFactory.RunAsync(delegate + JoinableTask? outer = hiPriFactory.RunAsync(delegate { Task.Run(async delegate { @@ -2695,7 +2732,7 @@ var outerFinished = new AsyncManualResetEvent(allowInliningAwaiters: true); JoinableTask innerTask; - var outer = hiPriFactory.RunAsync(delegate + JoinableTask? outer = hiPriFactory.RunAsync(delegate { innerTask = loPriFactory.RunAsync(async delegate { @@ -2722,7 +2759,7 @@ // For this test, we intentionally use each factory twice in a row. // We mix up the order in another test. - var outer = hiPriFactory.RunAsync(async delegate + JoinableTask? outer = hiPriFactory.RunAsync(async delegate { await hiPriFactory.RunAsync(async delegate { @@ -2756,7 +2793,7 @@ var hiPriFactory = new ModalPumpingJoinableTaskFactory(this.context); // In this particular test, we intentionally mix up the JTFs in hi-lo-hi-lo order. - var outer = hiPriFactory.RunAsync(async delegate + JoinableTask? outer = hiPriFactory.RunAsync(async delegate { await loPriFactory.RunAsync(async delegate { @@ -2845,8 +2882,8 @@ { int outstandingMessages = 0; var cts = new CancellationTokenSource(1000); - var collection2 = this.asyncPump.Context.CreateCollection(); - var pump2 = this.asyncPump.Context.CreateFactory(collection2); + JoinableTaskCollection? collection2 = this.asyncPump.Context.CreateCollection(); + JoinableTaskFactory? pump2 = this.asyncPump.Context.CreateFactory(collection2); Task? t1 = null, t2 = null; ((DerivedJoinableTaskFactory)this.asyncPump).AssumeConcurrentUse = true; @@ -2860,7 +2897,7 @@ { while (!cts.IsCancellationRequested) { - var awaiter = pump2.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = pump2.SwitchToMainThreadAsync().GetAwaiter(); Interlocked.Increment(ref outstandingMessages); awaiter.OnCompleted(delegate { @@ -2884,7 +2921,7 @@ { while (!cts.IsCancellationRequested) { - var awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); + JoinableTaskFactory.MainThreadAwaiter awaiter = this.asyncPump.SwitchToMainThreadAsync().GetAwaiter(); Interlocked.Increment(ref outstandingMessages); awaiter.OnCompleted(delegate { @@ -2975,7 +3012,7 @@ await Task.Yield(); }); - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); await Task.Yield(); @@ -3037,7 +3074,7 @@ }); }); - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { Assert.Equal(otherThreadId, Environment.CurrentManagedThreadId); await Task.Yield(); @@ -3118,8 +3155,8 @@ { try { - var otherCollection = this.context.CreateCollection(); - var otherPump = this.context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.context.CreateCollection(); + JoinableTaskFactory? otherPump = this.context.CreateFactory(otherCollection); await otherPump.SwitchToMainThreadAsync(); Assert.Equal(this.originalThreadManagedId, Environment.CurrentManagedThreadId); } @@ -3147,15 +3184,15 @@ [Fact] public void JoinTwice() { - var joinable = this.asyncPump.RunAsync(async delegate + JoinableTask? joinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); }); this.asyncPump.Run(async delegate { - var task1 = joinable.JoinAsync(); - var task2 = joinable.JoinAsync(); + Task? task1 = joinable.JoinAsync(); + Task? task2 = joinable.JoinAsync(); await Task.WhenAll(task1, task2); }); } @@ -3163,12 +3200,12 @@ [Fact] public void GrandparentJoins() { - var innerJoinable = this.asyncPump.RunAsync(async delegate + JoinableTask? innerJoinable = this.asyncPump.RunAsync(async delegate { await Task.Yield(); }); - var outerJoinable = this.asyncPump.RunAsync(async delegate + JoinableTask? outerJoinable = this.asyncPump.RunAsync(async delegate { await innerJoinable; }); @@ -3182,13 +3219,15 @@ { if (this.ExecuteInIsolation()) { - this.CheckGCPressure(delegate - { - this.asyncPump.Run(delegate + this.CheckGCPressure( + delegate { - return Task.CompletedTask; - }); - }, maxBytesAllocated: 819); + this.asyncPump.Run(delegate + { + return Task.CompletedTask; + }); + }, + maxBytesAllocated: 819); } } @@ -3199,13 +3238,15 @@ if (this.ExecuteInIsolation()) { - this.CheckGCPressure(delegate - { - this.asyncPump.Run(delegate + this.CheckGCPressure( + delegate { - return completedTask; - }); - }, maxBytesAllocated: 901); + this.asyncPump.Run(delegate + { + return completedTask; + }); + }, + maxBytesAllocated: 901); } } @@ -3214,13 +3255,15 @@ { if (this.ExecuteInIsolation()) { - this.CheckGCPressure(delegate - { - this.asyncPump.Run(async delegate + this.CheckGCPressure( + delegate { - await Task.Yield(); - }); - }, maxBytesAllocated: 2457); + this.asyncPump.Run(async delegate + { + await Task.Yield(); + }); + }, + maxBytesAllocated: 2457); } } @@ -3229,13 +3272,16 @@ { if (this.ExecuteInIsolation()) { - this.CheckGCPressure(delegate - { - this.asyncPump.Run(async delegate + this.CheckGCPressure( + delegate { - await Task.Yield(); - }); - }, maxBytesAllocated: 2457); + this.asyncPump.Run( + async delegate + { + await Task.Yield(); + }); + }, + maxBytesAllocated: 2457); } } #endif @@ -3249,7 +3295,7 @@ [Fact] public void NestedRunSynchronouslyOuterDoesNotStealWorkFromNested() { - var collection = this.context.CreateCollection(); + JoinableTaskCollection? collection = this.context.CreateCollection(); var asyncPump = new COMReentrantJoinableTaskFactory(collection); var nestedWorkBegun = new AsyncManualResetEvent(); asyncPump.ReenterWaitWith(() => @@ -3272,13 +3318,13 @@ public void RunAsyncExceptionsCapturedInResult() { var exception = new InvalidOperationException(); - var joinableTask = this.asyncPump.RunAsync(delegate + JoinableTask? joinableTask = this.asyncPump.RunAsync(delegate { throw exception; }); Assert.True(joinableTask.IsCompleted); Assert.Same(exception, joinableTask.Task.Exception?.InnerException); - var awaiter = joinableTask.GetAwaiter(); + TaskAwaiter awaiter = joinableTask.GetAwaiter(); try { awaiter.GetResult(); @@ -3294,13 +3340,13 @@ public void RunAsyncOfTExceptionsCapturedInResult() { var exception = new InvalidOperationException(); - var joinableTask = this.asyncPump.RunAsync(delegate + JoinableTask? joinableTask = this.asyncPump.RunAsync(delegate { throw exception; }); Assert.True(joinableTask.IsCompleted); Assert.Same(exception, joinableTask.Task.Exception!.InnerException); - var awaiter = joinableTask.GetAwaiter(); + TaskAwaiter awaiter = joinableTask.GetAwaiter(); try { awaiter.GetResult(); @@ -3373,7 +3419,7 @@ GC.Collect(); weakResult.TryGetTarget(out object? target); - Assert.Null(target); //, "The task's result should be collected unless the JoinableTask is leaked"); + Assert.Null(target); // The task's result should be collected unless the JoinableTask is leaked } [Fact] @@ -3400,7 +3446,7 @@ // It will complete only when the background thread works (aka. MainThreadAWaiter.OnCompleted()) are done, // and then we will signal a test event to resume the main thread execution, to let the remaining parts // in the async delegate go through. - var joinable = this.asyncPump.RunAsync(async () => + JoinableTask? joinable = this.asyncPump.RunAsync(async () => { await this.asyncPump.SwitchToMainThreadAsync(cts.Token); return result; @@ -3430,7 +3476,8 @@ } target = null; - } while (!this.TimeoutToken.IsCancellationRequested); + } + while (!this.TimeoutToken.IsCancellationRequested); Assert.Null(target); // The task's result should be collected unless the JoinableTask is leaked } @@ -3467,7 +3514,7 @@ this.context.Factory.Run(async delegate { await jtStarted; - var joinTask = this.joinableCollection!.JoinTillEmptyAsync(); + Task? joinTask = this.joinableCollection!.JoinTillEmptyAsync(); await joinTask.WithTimeout(UnexpectedTimeout); Assert.True(joinTask.IsCompleted); await unawaitedWork!; @@ -3515,7 +3562,7 @@ }); this.context.Factory.Run(async delegate { - var joinTask = this.joinableCollection!.JoinTillEmptyAsync(); + Task? joinTask = this.joinableCollection!.JoinTillEmptyAsync(); await joinTask.WithTimeout(UnexpectedTimeout); Assert.True(joinTask.IsCompleted); }); @@ -3543,12 +3590,12 @@ [Fact] public void JoinAsyncShouldCompleteWithoutUIThreadAfterCancellation() { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); }); var cts = new CancellationTokenSource(); - var joinTask = jt.JoinAsync(cts.Token); + Task? joinTask = jt.JoinAsync(cts.Token); cts.Cancel(); // We expect to be able to block on the UI thread and the Task complete. @@ -3561,11 +3608,11 @@ public void JoinAsyncShouldCompleteWithoutUIThreadAfterTaskCompletes() { var mre = new AsyncManualResetEvent(); - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await mre.WaitAsync().ConfigureAwait(false); }); - var joinTask = jt.JoinAsync(); + Task? joinTask = jt.JoinAsync(); mre.Set(); Assert.True(joinTask.Wait(AsyncDelay)); } @@ -3573,13 +3620,13 @@ [Fact] public void JoinAsyncOfTShouldCompleteWithoutUIThreadAfterCancellation() { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); return 2; }); var cts = new CancellationTokenSource(); - var joinTask = jt.JoinAsync(cts.Token); + Task? joinTask = jt.JoinAsync(cts.Token); cts.Cancel(); // We expect to be able to block on the UI thread and the Task complete. @@ -3592,12 +3639,12 @@ public void JoinAsyncOfTShouldCompleteWithoutUIThreadAfterTaskCompletes() { var mre = new AsyncManualResetEvent(); - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await mre.WaitAsync().ConfigureAwait(false); return 2; }); - var joinTask = jt.JoinAsync(); + Task? joinTask = jt.JoinAsync(); mre.Set(); Assert.True(joinTask.Wait(AsyncDelay)); } @@ -3607,7 +3654,7 @@ { using (TestUtilities.StarveThreadpool()) { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); }); @@ -3620,7 +3667,7 @@ { using (TestUtilities.StarveThreadpool()) { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); return 1; @@ -3634,7 +3681,7 @@ { using (TestUtilities.StarveThreadpool()) { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); }); @@ -3650,7 +3697,7 @@ { using (TestUtilities.StarveThreadpool()) { - var jt = this.asyncPump.RunAsync(async delegate + JoinableTask? jt = this.asyncPump.RunAsync(async delegate { await Task.Yield(); return 1; @@ -3735,7 +3782,7 @@ unblockJoinableTask.Set(); } }); - var joinableTask = this.asyncPump.RunAsync(delegate + JoinableTask? joinableTask = this.asyncPump.RunAsync(delegate { joinableTaskStarted.Set(); @@ -3774,7 +3821,7 @@ unblockJoinableTask.Set(); } }); - var joinableTask = this.asyncPump.RunAsync(delegate + JoinableTask? joinableTask = this.asyncPump.RunAsync(delegate { joinableTaskStarted.Set(); @@ -3809,8 +3856,8 @@ private Task SomeOperationThatUsesMainThreadViaItsOwnAsyncPumpAsync() { - var otherCollection = this.context.CreateCollection(); - var privateAsyncPump = this.context.CreateFactory(otherCollection); + JoinableTaskCollection? otherCollection = this.context.CreateCollection(); + JoinableTaskFactory? privateAsyncPump = this.context.CreateFactory(otherCollection); return Task.Run(async delegate { await Task.Yield(); @@ -3843,7 +3890,7 @@ // Await an extra bit of time to allow for unexpected reentrancy to occur while the // main thread is only synchronously blocking. - var waitTask = unrelatedMainThreadWorkInvoked.WaitAsync(); + Task? waitTask = unrelatedMainThreadWorkInvoked.WaitAsync(); Assert.NotSame( waitTask, await Task.WhenAny(waitTask, Task.Delay(AsyncDelay / 2))); // Background work completed work on the UI thread before it was invited to do so. @@ -3906,7 +3953,7 @@ var innerFactory = new ModalPumpingJoinableTaskFactory(this.context); JoinableTask? inner = null; - var outer = outerFactory.RunAsync(async delegate + JoinableTask? outer = outerFactory.RunAsync(async delegate { inner = innerFactory.RunAsync(async delegate { @@ -3932,7 +3979,7 @@ private WeakReference JoinableTaskReleasedBySyncContextAfterCompletion_Helper(out SynchronizationContext? syncContext) { SynchronizationContext? sc = null; - var job = this.asyncPump.RunAsync(() => + JoinableTask? job = this.asyncPump.RunAsync(() => { sc = SynchronizationContext.Current; // simulate someone who has captured the sync context. return Task.CompletedTask; @@ -3976,7 +4023,7 @@ { context = context ?? this.context; IHangReportContributor contributor = context; - var report = contributor.GetHangReport(); + HangReportContribution? report = contributor.GetHangReport(); this.Logger.WriteLine("DGML task graph"); this.Logger.WriteLine(report.Content); } @@ -4031,9 +4078,9 @@ protected override void WaitSynchronously(Task task) { - if (this.action != null) + if (this.action is object) { - var action = this.action; + Action? action = this.action; this.action = null; action(); } @@ -4130,7 +4177,7 @@ if (!this.AssumeConcurrentUse) { - Assert.Equal(this.TransitionedToMainThreadHitCount + 1, this.TransitioningToMainThreadHitCount); //, "Imbalance of transition events."); + Assert.Equal(this.TransitionedToMainThreadHitCount + 1, this.TransitioningToMainThreadHitCount); // Imbalance of transition events. } this.TransitioningToMainThreadCallback?.Invoke(joinableTask); @@ -4162,7 +4209,7 @@ if (!this.AssumeConcurrentUse) { - Assert.Equal(this.TransitionedToMainThreadHitCount, this.TransitioningToMainThreadHitCount); //, "Imbalance of transition events."); + Assert.Equal(this.TransitionedToMainThreadHitCount, this.TransitioningToMainThreadHitCount); // Imbalance of transition events. } this.TransitionedToMainThreadCallback?.Invoke(joinableTask); @@ -4221,12 +4268,14 @@ cancellationToken.ThrowIfCancellationRequested(); } - WaitHandle.WaitAny(new WaitHandle[] { + WaitHandle.WaitAny(new WaitHandle[] + { cancellationToken.WaitHandle, this.messageQueued, ((IAsyncResult)task).AsyncWaitHandle, }); - } while (!task.IsCompleted || this.queuedMessages.Count > 0); + } + while (!task.IsCompleted || this.queuedMessages.Count > 0); } protected override void PostToUnderlyingSynchronizationContext(SendOrPostCallback callback, object state) @@ -4256,7 +4305,7 @@ internal async Task OperationAsync() { await this.pump.SwitchToMainThreadAsync(); - if (this.dependentService != null) + if (this.dependentService is object) { await (this.dependentTask = this.dependentService.OperationAsync()); } @@ -4269,7 +4318,7 @@ internal async Task StopAsync(Task operation) { Requires.NotNull(operation, nameof(operation)); - if (this.dependentService != null) + if (this.dependentService is object) { await this.dependentService.StopAsync(this.dependentTask!); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs index b68cbf87..e0351984 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ListOfOftenOneTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -21,7 +24,7 @@ [Fact] public void EnumerationOfEmpty() { - using (var enumerator = this.list.GetEnumerator()) + using (ListOfOftenOne.Enumerator enumerator = this.list.GetEnumerator()) { Assert.False(enumerator.MoveNext()); enumerator.Reset(); @@ -33,7 +36,7 @@ public void EnumerationOfOne() { this.list.Add(new GenericParameterHelper(1)); - using (var enumerator = this.list.GetEnumerator()) + using (ListOfOftenOne.Enumerator enumerator = this.list.GetEnumerator()) { Assert.True(enumerator.MoveNext()); Assert.Equal(1, enumerator.Current.Data); @@ -50,7 +53,7 @@ { this.list.Add(new GenericParameterHelper(1)); this.list.Add(new GenericParameterHelper(2)); - using (var enumerator = this.list.GetEnumerator()) + using (ListOfOftenOne.Enumerator enumerator = this.list.GetEnumerator()) { Assert.True(enumerator.MoveNext()); Assert.Equal(1, enumerator.Current.Data); @@ -132,7 +135,7 @@ { this.list.Add(new GenericParameterHelper(1)); - using (var enumerator = this.list.EnumerateAndClear()) + using (ListOfOftenOne.Enumerator enumerator = this.list.EnumerateAndClear()) { Assert.Empty(this.list.ToArray()); // The collection should have been cleared. Assert.True(enumerator.MoveNext()); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj b/test/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj similarity index 75% rename from src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj rename to test/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj index ee3b1b0a..cb900909 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj +++ b/test/Microsoft.VisualStudio.Threading.Tests/Microsoft.VisualStudio.Threading.Tests.csproj @@ -1,11 +1,7 @@  net472;netcoreapp2.1;netcoreapp3.1 - true - Microsoft.VisualStudio.Threading.Tests.ruleset - $(NoWarn);CS1591 true - false true true @@ -14,15 +10,15 @@ $(DefineConstants);ISOLATED_TEST_SUPPORT - + InternalUtilities.cs - - ListOfOftenOne.cs + + ListOfOftenOne`1.cs - - - WeakKeyDictionary.cs + + + WeakKeyDictionary`2.cs @@ -44,7 +40,7 @@ - + diff --git a/src/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs similarity index 71% rename from src/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs index 55d5480c..ecf4e300 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/NonConcurrentSynchronizationContextTests.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -21,7 +24,7 @@ public class NonConcurrentSynchronizationContextTests : TestBase [Fact] public void CreateCopy() { - var copy = this.nonSticky.CreateCopy(); + SynchronizationContext? copy = this.nonSticky.CreateCopy(); Assert.NotSame(this.nonSticky, copy); ConfirmNonConcurrentPost(this.nonSticky, copy); } @@ -51,7 +54,7 @@ public class NonConcurrentSynchronizationContextTests : TestBase var eventArgs = new TaskCompletionSource<(object?, Exception)>(); this.nonSticky.UnhandledException += (s, e) => eventArgs.SetResult((s, e)); this.nonSticky.Post(s => throw new InvalidOperationException(), null); - var (sender, ex) = await eventArgs.Task.WithCancellation(this.TimeoutToken); + (object sender, Exception ex) = await eventArgs.Task.WithCancellation(this.TimeoutToken); Assert.Same(this.nonSticky, sender); Assert.IsType(ex); } @@ -74,23 +77,27 @@ public class NonConcurrentSynchronizationContextTests : TestBase { var sticky = new NonConcurrentSynchronizationContext(sticky: true); var tcs = new TaskCompletionSource(); - sticky.Post(_ => - { - try + sticky.Post( + _ => { - SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - sticky.Send(_ => + try { - Assert.Same(sticky, SynchronizationContext.Current); - }, null); - Assert.IsType(SynchronizationContext.Current); - tcs.TrySetResult(null); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + sticky.Send( + _ => + { + Assert.Same(sticky, SynchronizationContext.Current); + }, + null); + Assert.IsType(SynchronizationContext.Current); + tcs.TrySetResult(null); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + }, + null); await tcs.Task.WithCancellation(this.TimeoutToken); } @@ -98,23 +105,27 @@ public class NonConcurrentSynchronizationContextTests : TestBase public async Task InlinedSendIgnoresSyncContextWhenNotSticky() { var tcs = new TaskCompletionSource(); - this.nonSticky.Post(_ => - { - try + this.nonSticky.Post( + _ => { - SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); - this.nonSticky.Send(_ => + try { + SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); + this.nonSticky.Send( + _ => + { + Assert.IsType(SynchronizationContext.Current); + }, + null); Assert.IsType(SynchronizationContext.Current); - }, null); - Assert.IsType(SynchronizationContext.Current); - tcs.TrySetResult(null); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); + tcs.TrySetResult(null); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + }, + null); await tcs.Task.WithCancellation(this.TimeoutToken); } @@ -155,13 +166,17 @@ public class NonConcurrentSynchronizationContextTests : TestBase Task.Run(delegate { bool reachedInnerDelegate = false; - this.nonSticky.Send(_ => - { - this.nonSticky.Send(_ => + this.nonSticky.Send( + _ => { - reachedInnerDelegate = true; - }, null); - }, null); + this.nonSticky.Send( + _ => + { + reachedInnerDelegate = true; + }, + null); + }, + null); Assert.True(reachedInnerDelegate); }).WithCancellation(this.TimeoutToken).GetAwaiter().GetResult(); @@ -171,13 +186,17 @@ public class NonConcurrentSynchronizationContextTests : TestBase public async Task SendWithinPostDoesNotDeadlock() { var reachedInnerDelegate = new TaskCompletionSource(); - this.nonSticky.Post(_ => - { - this.nonSticky.Send(_ => + this.nonSticky.Post( + _ => { - reachedInnerDelegate.SetResult(true); - }, null); - }, null); + this.nonSticky.Send( + _ => + { + reachedInnerDelegate.SetResult(true); + }, + null); + }, + null); Assert.True(await reachedInnerDelegate.Task.WithCancellation(this.TimeoutToken)); } @@ -186,18 +205,22 @@ public class NonConcurrentSynchronizationContextTests : TestBase public void CannotFoolSendBySettingSyncContext() { using var releaseFirst = new ManualResetEventSlim(); - this.nonSticky.Post(s => - { - releaseFirst.Wait(UnexpectedTimeout); - }, null); + this.nonSticky.Post( + s => + { + releaseFirst.Wait(UnexpectedTimeout); + }, + null); using var secondEntered = new ManualResetEventSlim(); Task sendTask = Task.Run(delegate { SynchronizationContext.SetSynchronizationContext(this.nonSticky); - this.nonSticky.Send(s => - { - secondEntered.Set(); - }, null); + this.nonSticky.Send( + s => + { + secondEntered.Set(); + }, + null); }); Assert.False(secondEntered.Wait(ExpectedTimeout)); @@ -222,17 +245,21 @@ public class NonConcurrentSynchronizationContextTests : TestBase private static void ConfirmNonConcurrentSend(SynchronizationContext ctxt) { using var releaseFirst = new ManualResetEventSlim(); - ctxt.Post(s => - { - releaseFirst.Wait(UnexpectedTimeout); - }, null); + ctxt.Post( + s => + { + releaseFirst.Wait(UnexpectedTimeout); + }, + null); using var secondEntered = new ManualResetEventSlim(); Task sendTask = Task.Run(delegate { - ctxt.Send(s => - { - secondEntered.Set(); - }, null); + ctxt.Send( + s => + { + secondEntered.Set(); + }, + null); }); Assert.False(secondEntered.Wait(ExpectedTimeout)); @@ -248,15 +275,19 @@ public class NonConcurrentSynchronizationContextTests : TestBase // by scheduling something on each one, and confirming that the second can't start // before the second one completes. using var releaseFirst = new ManualResetEventSlim(); - a.Post(s => - { - releaseFirst.Wait(UnexpectedTimeout); - }, null); + a.Post( + s => + { + releaseFirst.Wait(UnexpectedTimeout); + }, + null); using var secondEntered = new ManualResetEventSlim(); - b.Post(s => - { - secondEntered.Set(); - }, null); + b.Post( + s => + { + secondEntered.Set(); + }, + null); Assert.False(secondEntered.Wait(ExpectedTimeout)); // Now that we've proven the second one hasn't started, allow the first to finish and confirm the second one could then execute. diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs similarity index 83% rename from src/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs index 22455f3f..2d194c42 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ProgressWithCompletionTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -60,7 +63,7 @@ IProgress reporter = progress; reporter.Report(new GenericParameterHelper(1)); - var progressAwaitable = progress.WaitAsync(); + Task? progressAwaitable = progress.WaitAsync(); Assert.False(progressAwaitable.GetAwaiter().IsCompleted); await Task.Delay(AsyncDelay); Assert.False(progressAwaitable.GetAwaiter().IsCompleted); @@ -83,7 +86,7 @@ reporter.Report(new GenericParameterHelper(1)); var cts = new CancellationTokenSource(); - var progressAwaitable = progress.WaitAsync(cts.Token); + Task? progressAwaitable = progress.WaitAsync(cts.Token); await Task.Delay(AsyncDelay); Assert.False(progressAwaitable.GetAwaiter().IsCompleted); cts.Cancel(); @@ -96,10 +99,10 @@ [Fact] public void SynchronizationContextCaptured() { - var syncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? syncContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncContext); TaskCompletionSource callbackResult = new TaskCompletionSource(); - var frame = SingleThreadedTestSynchronizationContext.NewFrame(); + SingleThreadedTestSynchronizationContext.IFrame? frame = SingleThreadedTestSynchronizationContext.NewFrame(); var callback = new Action( p => { @@ -131,27 +134,29 @@ [PairwiseData] public void DoesNotDeadlockWhenCallbackCapturesSyncContext(bool captureMainThreadContext) { - var syncContext = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? syncContext = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncContext); var jtc = new JoinableTaskContext(); const int expectedCallbackValue = 1; var actualCallbackValue = new TaskCompletionSource(); - Func> progressFactory = () => new ProgressWithCompletion(async arg => - { - try + Func> progressFactory = () => new ProgressWithCompletion( + async arg => { - Assert.Equal(captureMainThreadContext, jtc.IsOnMainThread); + try + { + Assert.Equal(captureMainThreadContext, jtc.IsOnMainThread); - // Ensure we have a main thread dependency even if we started on a threadpool thread. - await jtc.Factory.SwitchToMainThreadAsync(this.TimeoutToken); - actualCallbackValue.SetResult(arg.Data); - } - catch (Exception ex) - { - actualCallbackValue.SetException(ex); - } - }, jtc.Factory); + // Ensure we have a main thread dependency even if we started on a threadpool thread. + await jtc.Factory.SwitchToMainThreadAsync(this.TimeoutToken); + actualCallbackValue.SetResult(arg.Data); + } + catch (Exception ex) + { + actualCallbackValue.SetException(ex); + } + }, + jtc.Factory); ProgressWithCompletion progress; if (captureMainThreadContext) diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs index 482eaee9..1fa81aa2 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreJTFTests.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -95,12 +98,12 @@ public class ReentrantSemaphoreJTFTests : ReentrantSemaphoreTestBase // The goal is to test that the 3rd, sync request, will release the UI thread // to the two async requests, then it will resume its operations. await this.joinableTaskContext!.Factory.SwitchToMainThreadAsync(); - var secondOperation = this.joinableTaskContext.Factory.RunAsync(() => this.AcquireSemaphoreAsync(this.TimeoutToken)); - var thirdOperation = this.AcquireSemaphoreAsync(this.TimeoutToken); + JoinableTask? secondOperation = this.joinableTaskContext.Factory.RunAsync(() => this.AcquireSemaphoreAsync(this.TimeoutToken)); + Task? thirdOperation = this.AcquireSemaphoreAsync(this.TimeoutToken); bool finalSemaphoreAcquired = this.joinableTaskContext.Factory.Run( () => { - var semaphoreTask = this.AcquireSemaphoreAsync(this.TimeoutToken); + Task? semaphoreTask = this.AcquireSemaphoreAsync(this.TimeoutToken); continueFirstOperation.Set(); return semaphoreTask; }); @@ -136,7 +139,7 @@ public class ReentrantSemaphoreJTFTests : ReentrantSemaphoreTestBase { await release1; - var operation2 = this.AcquireSemaphoreAsync(abortSemaphore.Token); + Task? operation2 = this.AcquireSemaphoreAsync(abortSemaphore.Token); this.joinableTaskContext!.Factory.Run( async () => @@ -157,7 +160,7 @@ public class ReentrantSemaphoreJTFTests : ReentrantSemaphoreTestBase protected override ReentrantSemaphore CreateSemaphore(ReentrantSemaphore.ReentrancyMode mode, int initialCount = 1) { - if (this.joinableTaskContext == null) + if (this.joinableTaskContext is null) { using (this.Dispatcher.Apply()) { diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs similarity index 82% rename from src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs index 02d9700a..43e98141 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreNonJTFTests.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs similarity index 92% rename from src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs index 3b26c5f2..1224e82e 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -16,7 +19,9 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable /// protected static new readonly TimeSpan ExpectedTimeout = TimeSpan.FromMilliseconds(250); // faster than the base class since we use this a lot +#pragma warning disable CA2213 protected ReentrantSemaphore? semaphore; +#pragma warning restore CA2213 public ReentrantSemaphoreTestBase(ITestOutputHelper logger) : base(logger) @@ -102,10 +107,10 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable this.ExecuteOnDispatcher(async delegate { var releaseHolder = new AsyncManualResetEvent(); - var holder = this.semaphore.ExecuteAsync(() => releaseHolder.WaitAsync()); + Task? holder = this.semaphore.ExecuteAsync(() => releaseHolder.WaitAsync()); bool executed = false; - var waiter = this.semaphore.ExecuteAsync( + Task? waiter = this.semaphore.ExecuteAsync( delegate { Assert.Equal(originalThreadId, Environment.CurrentManagedThreadId); @@ -149,10 +154,10 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable this.ExecuteOnDispatcher(async delegate { var releaseHolder = new AsyncManualResetEvent(); - var holder = this.semaphore.ExecuteAsync(() => releaseHolder.WaitAsync()); + Task? holder = this.semaphore.ExecuteAsync(() => releaseHolder.WaitAsync()); bool executed = false; - var waiter = this.semaphore.ExecuteAsync( + ValueTask waiter = this.semaphore.ExecuteAsync( delegate { Assert.Equal(originalThreadId, Environment.CurrentManagedThreadId); @@ -200,7 +205,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable var firstRelease = new AsyncManualResetEvent(); var secondEntered = new AsyncManualResetEvent(); - var firstOperation = this.semaphore.ExecuteAsync( + Task? firstOperation = this.semaphore.ExecuteAsync( async delegate { firstEntered.Set(); @@ -208,7 +213,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable }, this.TimeoutToken); - var secondOperation = this.semaphore.ExecuteAsync( + Task? secondOperation = this.semaphore.ExecuteAsync( delegate { secondEntered.Set(); @@ -232,7 +237,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable { this.semaphore = this.CreateSemaphore(initialCount: initialCount); - var releasers = Enumerable.Range(0, initialCount).Select(i => new AsyncManualResetEvent()).ToArray(); + AsyncManualResetEvent[]? releasers = Enumerable.Range(0, initialCount).Select(i => new AsyncManualResetEvent()).ToArray(); var operations = new Task[initialCount]; for (int i = 0; i < 5; i++) { @@ -243,7 +248,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable operations[j] = this.semaphore.ExecuteAsync(() => releasers[k].WaitAsync(), this.TimeoutToken); } - var releaseSequence = Enumerable.Range(0, initialCount); + IEnumerable? releaseSequence = Enumerable.Range(0, initialCount); // We'll test both releasing in FIFO and LIFO order. if (i % 2 == 0) @@ -266,19 +271,26 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable public void Reentrant(ReentrantSemaphore.ReentrancyMode mode) { this.semaphore = this.CreateSemaphore(mode); - this.ExecuteOnDispatcher(async delegate - { - await this.semaphore.ExecuteAsync(async delegate + this.ExecuteOnDispatcher( + async delegate { - await this.semaphore.ExecuteAsync(async delegate - { - await this.semaphore.ExecuteAsync(delegate + await this.semaphore.ExecuteAsync( + async delegate { - return Task.CompletedTask; - }, this.TimeoutToken); - }, this.TimeoutToken); - }, this.TimeoutToken); - }); + await this.semaphore.ExecuteAsync( + async delegate + { + await this.semaphore.ExecuteAsync( + delegate + { + return Task.CompletedTask; + }, + this.TimeoutToken); + }, + this.TimeoutToken); + }, + this.TimeoutToken); + }); } [Fact] @@ -429,10 +441,10 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable this.ExecuteOnDispatcher(async delegate { var release = new AsyncManualResetEvent(); - var holder = this.semaphore.ExecuteAsync(() => release.WaitAsync(), this.TimeoutToken); + Task? holder = this.semaphore.ExecuteAsync(() => release.WaitAsync(), this.TimeoutToken); var cts = CancellationTokenSource.CreateLinkedTokenSource(this.TimeoutToken); - var waiter = this.semaphore.ExecuteAsync(() => Task.CompletedTask, cts.Token); + Task? waiter = this.semaphore.ExecuteAsync(() => Task.CompletedTask, cts.Token); Assert.False(waiter.IsCompleted); cts.Cancel(); await Assert.ThrowsAnyAsync(() => waiter).WithCancellation(this.TimeoutToken); @@ -595,7 +607,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable { this.semaphore = this.CreateSemaphore(mode); var releaseFirstHolder = new AsyncManualResetEvent(); - var holder = this.semaphore.ExecuteAsync(() => releaseFirstHolder.WaitAsync()); + Task? holder = this.semaphore.ExecuteAsync(() => releaseFirstHolder.WaitAsync()); const int waiterCount = 5; var cts = new CancellationTokenSource[waiterCount]; @@ -643,7 +655,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable this.ExecuteOnDispatcher( async () => { - var semaphore = this.CreateSemaphore(ReentrantSemaphore.ReentrancyMode.Stack); + ReentrantSemaphore? semaphore = this.CreateSemaphore(ReentrantSemaphore.ReentrancyMode.Stack); var releaser1 = new AsyncManualResetEvent(); var releaser2 = new AsyncManualResetEvent(); @@ -673,7 +685,7 @@ public abstract class ReentrantSemaphoreTestBase : TestBase, IDisposable }); await releaser3.WaitAsync(); - var pendingSemaphoreTask = semaphore.ExecuteAsync(() => Task.CompletedTask); + Task? pendingSemaphoreTask = semaphore.ExecuteAsync(() => Task.CompletedTask); releaser1.Set(); await Assert.ThrowsAsync(() => outerFaultySemaphoreTask).WithCancellation(this.TimeoutToken); diff --git a/src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs index 6c89aaa5..be7c6897 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedSynchronizationContextTests.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; @@ -33,7 +30,7 @@ public class SingleThreadedSynchronizationContextTests : TestBase public void CreateCopy_ReturnsNewInstance() { var syncContext = new SingleThreadedSynchronizationContext(); - var other = syncContext.CreateCopy(); + System.Threading.SynchronizationContext? other = syncContext.CreateCopy(); Assert.NotSame(syncContext, other); // Verify that posting to the copy effectively gets the work to run on the original thread. @@ -68,7 +65,7 @@ public class SingleThreadedSynchronizationContextTests : TestBase { var syncContext = new SingleThreadedSynchronizationContext(); Exception expected = new InvalidOperationException(); - var actual = Assert.Throws(() => syncContext.Send(s => throw expected, null)); + TargetInvocationException? actual = Assert.Throws(() => syncContext.Send(s => throw expected, null)); Assert.Same(expected, actual.InnerException); } @@ -109,7 +106,7 @@ public class SingleThreadedSynchronizationContextTests : TestBase try { var expectedException = new InvalidOperationException(); - var actualException = Assert.Throws(() => syncContext.Send(s => throw expectedException, null)); + TargetInvocationException? actualException = Assert.Throws(() => syncContext.Send(s => throw expectedException, null)); Assert.Same(expectedException, actualException.InnerException); } finally @@ -177,7 +174,7 @@ public class SingleThreadedSynchronizationContextTests : TestBase var expectedException = new InvalidOperationException(); syncContext.Post(state => throw expectedException, null); - var actualException = Assert.Throws(() => syncContext.PushFrame(frame)); + InvalidOperationException? actualException = Assert.Throws(() => syncContext.PushFrame(frame)); Assert.Same(expectedException, actualException); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs b/test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs similarity index 89% rename from src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs rename to test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs index c22f40d6..e5285abc 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/SingleThreadedTestSynchronizationContext.cs @@ -1,4 +1,7 @@ -#if NETFRAMEWORK +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if NETFRAMEWORK #define UseWpfContext #endif @@ -25,7 +28,9 @@ namespace Microsoft.VisualStudio.Threading.Tests { public interface IFrame { +#pragma warning disable CA1716 // Identifiers should not match keywords bool Continue { get; set; } +#pragma warning restore CA1716 // Identifiers should not match keywords } public static SynchronizationContext New() diff --git a/src/Microsoft.VisualStudio.Threading.Tests/TestBase.cs b/test/Microsoft.VisualStudio.Threading.Tests/TestBase.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading.Tests/TestBase.cs rename to test/Microsoft.VisualStudio.Threading.Tests/TestBase.cs index 000d2368..4f499c6c 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/TestBase.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/TestBase.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Diagnostics; @@ -63,7 +66,7 @@ Requires.NotNull(completingAction, nameof(completingAction)); var completingActionFinished = new ManualResetEventSlim(); - var continuation = antecedent.ContinueWith( + Task? continuation = antecedent.ContinueWith( _ => Assert.True(completingActionFinished.Wait(AsyncDelay)), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, @@ -86,7 +89,7 @@ Requires.NotNull(completingAction, nameof(completingAction)); Thread callingThread = Thread.CurrentThread; - var continuation = antecedent.ContinueWith( + Task? continuation = antecedent.ContinueWith( _ => Assert.Equal(callingThread, Thread.CurrentThread), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, @@ -197,7 +200,7 @@ // This allows any posted messages that are now obsolete to be released. if (SingleThreadedTestSynchronizationContext.IsSingleThreadedSyncContext(SynchronizationContext.Current)) { - var frame = SingleThreadedTestSynchronizationContext.NewFrame(); + SingleThreadedTestSynchronizationContext.IFrame? frame = SingleThreadedTestSynchronizationContext.NewFrame(); SynchronizationContext.Current.Post(state => frame.Continue = false, null); SingleThreadedTestSynchronizationContext.PushFrame(SynchronizationContext.Current, frame); } @@ -256,7 +259,7 @@ Requires.NotNull(action, nameof(action)); if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA - && SynchronizationContext.Current == null) + && SynchronizationContext.Current is null) { action(); return; @@ -277,7 +280,7 @@ staThread.SetApartmentState(ApartmentState.STA); staThread.Start(); staThread.Join(); - if (staFailure != null) + if (staFailure is object) { ExceptionDispatchInfo.Capture(staFailure).Throw(); // rethrow preserving callstack. } @@ -299,7 +302,7 @@ SynchronizationContext.SetSynchronizationContext(SingleThreadedTestSynchronizationContext.New()); } - var frame = SingleThreadedTestSynchronizationContext.NewFrame(); + SingleThreadedTestSynchronizationContext.IFrame? frame = SingleThreadedTestSynchronizationContext.NewFrame(); Exception? failure = null; SynchronizationContext.Current!.Post( async _ => @@ -320,7 +323,7 @@ null); SingleThreadedTestSynchronizationContext.PushFrame(SynchronizationContext.Current, frame); - if (failure != null) + if (failure is object) { ExceptionDispatchInfo.Capture(failure).Throw(); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs similarity index 95% rename from src/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs index fbc6c6ef..112c2f7b 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/TestBaseTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs b/test/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs similarity index 90% rename from src/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs rename to test/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs index 54c2d504..03f70419 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/TestUtilities.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -26,24 +29,24 @@ /// internal static void Run(Func func) { - if (func == null) + if (func is null) { throw new ArgumentNullException(nameof(func)); } - var prevCtx = SynchronizationContext.Current; + SynchronizationContext? prevCtx = SynchronizationContext.Current; try { - var syncCtx = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? syncCtx = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(syncCtx); - var t = func(); - if (t == null) + Task? t = func(); + if (t is null) { throw new InvalidOperationException(); } - var frame = SingleThreadedTestSynchronizationContext.NewFrame(); + SingleThreadedTestSynchronizationContext.IFrame? frame = SingleThreadedTestSynchronizationContext.NewFrame(); t.ContinueWith(_ => { frame.Continue = false; }, TaskScheduler.Default); SingleThreadedTestSynchronizationContext.PushFrame(syncCtx, frame); @@ -94,8 +97,8 @@ internal static DebugAssertionRevert DisableAssertionDialog() { #if NETFRAMEWORK - var listener = Debug.Listeners.OfType().FirstOrDefault(); - if (listener != null) + DefaultTraceListener? listener = Debug.Listeners.OfType().FirstOrDefault(); + if (listener is object) { listener.AssertUiEnabled = false; } @@ -189,7 +192,9 @@ /// /// Thrown if the isolated test result is a Failure. /// Thrown if on a platform that we do not yet support test isolation on. +#pragma warning disable CA1801 // Review unused parameters internal static Task ExecuteInIsolationAsync(string testClassName, string testMethodName, ITestOutputHelper logger) +#pragma warning restore CA1801 // Review unused parameters { Requires.NotNullOrEmpty(testClassName, nameof(testClassName)); Requires.NotNullOrEmpty(testMethodName, nameof(testMethodName)); @@ -208,8 +213,8 @@ testClassName, testMethodName)) { - RedirectStandardError = logger != null, - RedirectStandardOutput = logger != null, + RedirectStandardError = logger is object, + RedirectStandardOutput = logger is object, CreateNoWindow = true, UseShellExecute = false, }; @@ -219,12 +224,12 @@ StartInfo = startInfo, EnableRaisingEvents = true, }; - var processExitCode = new TaskCompletionSource(); + var processExitCode = new TaskCompletionSource(); isolatedTestProcess.Exited += (s, e) => { - processExitCode.SetResult((IsolatedTestHost.ExitCodes)isolatedTestProcess.ExitCode); + processExitCode.SetResult((IsolatedTestHost.ExitCode)isolatedTestProcess.ExitCode); }; - if (logger != null) + if (logger is object) { isolatedTestProcess.OutputDataReceived += (s, e) => logger.WriteLine(e.Data ?? string.Empty); isolatedTestProcess.ErrorDataReceived += (s, e) => logger.WriteLine(e.Data ?? string.Empty); @@ -232,7 +237,7 @@ Assert.True(isolatedTestProcess.Start()); - if (logger != null) + if (logger is object) { isolatedTestProcess.BeginOutputReadLine(); isolatedTestProcess.BeginErrorReadLine(); @@ -243,11 +248,11 @@ { switch (t.Result) { - case IsolatedTestHost.ExitCodes.TestSkipped: + case IsolatedTestHost.ExitCode.TestSkipped: throw new SkipException("Test skipped. See output of isolated task for details."); - case IsolatedTestHost.ExitCodes.TestPassed: + case IsolatedTestHost.ExitCode.TestPassed: default: - Assert.Equal(IsolatedTestHost.ExitCodes.TestPassed, t.Result); + Assert.Equal(IsolatedTestHost.ExitCode.TestPassed, t.Result); break; } @@ -271,7 +276,7 @@ if (!task.IsCompleted) { // Waiting on a continuation of a task won't ever inline the predecessor (in .NET 4.x anyway). - var continuation = task.ContinueWith(t => { }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); + Task? continuation = task.ContinueWith(t => { }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); continuation.Wait(); } @@ -345,17 +350,17 @@ public void OnCompleted(Action continuation) { - var that = this; + YieldAndNotifyAwaiter that = this; this.baseAwaiter.OnCompleted(delegate { - if (that.resumingSignal != null) + if (that.resumingSignal is object) { that.resumingSignal.Set(); } continuation(); }); - if (this.yieldingSignal != null) + if (this.yieldingSignal is object) { this.yieldingSignal.Set(); } @@ -371,8 +376,8 @@ public void Dispose() { #if NETFRAMEWORK - var listener = Debug.Listeners.OfType().FirstOrDefault(); - if (listener != null) + DefaultTraceListener? listener = Debug.Listeners.OfType().FirstOrDefault(); + if (listener is object) { listener.AssertUiEnabled = true; } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs similarity index 85% rename from src/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs index 6961ac20..b7622962 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/TestUtilitiesTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs similarity index 91% rename from src/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs index a43d36cc..5b86008b 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ThreadingToolsTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -125,7 +128,7 @@ var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(); cts.Cancel(); - var result = ((Task)tcs.Task).WithCancellation(cts.Token); + Task? result = ((Task)tcs.Task).WithCancellation(cts.Token); Assert.True(result.IsCanceled); // Verify that the CancellationToken that led to cancellation is tucked away in the returned Task. @@ -153,7 +156,7 @@ { var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(); - var t = tcs.Task.WithCancellation(cts.Token); + Task? t = tcs.Task.WithCancellation(cts.Token); Assert.False(t.IsCompleted); cts.Cancel(); Assert.Throws(() => @@ -165,7 +168,7 @@ { var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(); - var t = tcs.Task.WithCancellation(cts.Token); + Task? t = tcs.Task.WithCancellation(cts.Token); tcs.SetResult(new GenericParameterHelper()); Assert.Same(tcs.Task.Result, t.GetAwaiter().GetResult()); } @@ -173,7 +176,7 @@ [Fact] public void WithCancellationOfTNoDeadlockFromSyncContext() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(AsyncDelay / 4); @@ -191,7 +194,7 @@ [Fact] public void WithCancellationOfTNoncancelableNoDeadlockFromSyncContext() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); var tcs = new TaskCompletionSource(); Task.Run(async delegate @@ -207,7 +210,7 @@ { var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(); - var t = ((Task)tcs.Task).WithCancellation(cts.Token); + Task? t = ((Task)tcs.Task).WithCancellation(cts.Token); Assert.False(t.IsCompleted); cts.Cancel(); Assert.Throws(() => @@ -219,7 +222,7 @@ { var tcs = new TaskCompletionSource(); var cts = new CancellationTokenSource(); - var t = ((Task)tcs.Task).WithCancellation(cts.Token); + Task? t = ((Task)tcs.Task).WithCancellation(cts.Token); tcs.SetResult(new GenericParameterHelper()); t.GetAwaiter().GetResult(); } @@ -227,7 +230,7 @@ [Fact] public void WithCancellationNoDeadlockFromSyncContext_Canceled() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); WithCancellationSyncBlock(simulateCancellation: true); } @@ -235,7 +238,7 @@ [Fact] public void WithCancellationNoDeadlockFromSyncContext_Completed() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); WithCancellationSyncBlock(simulateCancellation: false); } @@ -243,7 +246,7 @@ [Fact] public void WithCancellationNoncancelableNoDeadlockFromSyncContext() { - var dispatcher = SingleThreadedTestSynchronizationContext.New(); + SynchronizationContext? dispatcher = SingleThreadedTestSynchronizationContext.New(); SynchronizationContext.SetSynchronizationContext(dispatcher); WithCancellationSyncBlockOnNoncancelableToken(); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs similarity index 93% rename from src/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs index d7d16b46..3afea268 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/TplExtensionsTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; @@ -39,7 +42,7 @@ var evt = new ManualResetEventSlim(); Action a = () => evt.Set(); var cts = new CancellationTokenSource(); - var result = Task.CompletedTask.AppendAction(a, TaskContinuationOptions.DenyChildAttach, cts.Token); + Task? result = Task.CompletedTask.AppendAction(a, TaskContinuationOptions.DenyChildAttach, cts.Token); Assert.NotNull(result); Assert.Equal(TaskContinuationOptions.DenyChildAttach, (TaskContinuationOptions)result.CreationOptions); Assert.True(evt.Wait(TestTimeout)); @@ -187,8 +190,8 @@ public void WaitWithoutInlining() { var sluggishScheduler = new SluggishInliningTaskScheduler(); - var originalThread = Thread.CurrentThread; - var task = Task.Factory.StartNew( + Thread? originalThread = Thread.CurrentThread; + Task? task = Task.Factory.StartNew( delegate { Assert.NotSame(originalThread, Thread.CurrentThread); @@ -208,8 +211,8 @@ public void GetResultWithoutInlining() { var sluggishScheduler = new SluggishInliningTaskScheduler(); - var originalThread = Thread.CurrentThread; - var task = Task.Factory.StartNew( + Thread? originalThread = Thread.CurrentThread; + Task? task = Task.Factory.StartNew( delegate { Assert.NotSame(originalThread, Thread.CurrentThread); @@ -235,7 +238,7 @@ var task = Task.Delay(200); // This must not complete before we call WaitWithoutInlining. var continuationUnblocked = new ManualResetEventSlim(); - var continuationTask = task.ContinueWith( + Task? continuationTask = task.ContinueWith( delegate { Assert.True(continuationUnblocked.Wait(UnexpectedTimeout)); @@ -262,7 +265,7 @@ { var tcs = new TaskCompletionSource(); tcs.SetException(new InvalidOperationException()); - var ex = Assert.Throws(() => tcs.Task.WaitWithoutInlining()); + AggregateException? ex = Assert.Throws(() => tcs.Task.WaitWithoutInlining()); ex.Handle(x => x is InvalidOperationException); } @@ -279,7 +282,7 @@ { var tcs = new TaskCompletionSource(); tcs.SetException(new InvalidOperationException()); - var ex = Assert.Throws(() => tcs.Task.GetResultWithoutInlining(throwOriginalException: false)); + AggregateException? ex = Assert.Throws(() => tcs.Task.GetResultWithoutInlining(throwOriginalException: false)); ex.Handle(x => x is InvalidOperationException); } @@ -312,7 +315,7 @@ { Task? attachedTask = null; int originalThreadId = Environment.CurrentManagedThreadId; - var task = Task.Factory.StartNew( + Task? task = Task.Factory.StartNew( delegate { attachedTask = Task.Factory.StartNew( @@ -335,7 +338,7 @@ public async Task NoThrowAwaitable() { var tcs = new TaskCompletionSource(); - var nothrowTask = tcs.Task.NoThrowAwaitable(); + TplExtensions.NoThrowTaskAwaitable nothrowTask = tcs.Task.NoThrowAwaitable(); Assert.False(nothrowTask.GetAwaiter().IsCompleted); tcs.SetException(new InvalidOperationException()); await nothrowTask; @@ -382,7 +385,7 @@ var awaitableTcs = new TaskCompletionSource(); var asyncLocal = new AsyncLocal(); asyncLocal.Value = "expected"; - var awaiter = awaitableTcs.Task.NoThrowAwaitable(captureContext).GetAwaiter(); + TplExtensions.NoThrowTaskAwaiter awaiter = awaitableTcs.Task.NoThrowAwaitable(captureContext).GetAwaiter(); awaiter.OnCompleted(delegate { try @@ -410,7 +413,7 @@ var awaitableTcs = new TaskCompletionSource(); var asyncLocal = new AsyncLocal(); asyncLocal.Value = "expected"; - var awaiter = awaitableTcs.Task.NoThrowAwaitable(captureContext).GetAwaiter(); + TplExtensions.NoThrowTaskAwaiter awaiter = awaitableTcs.Task.NoThrowAwaitable(captureContext).GetAwaiter(); awaiter.UnsafeOnCompleted(delegate { try @@ -434,7 +437,7 @@ public void InvokeAsyncNullEverything() { AsyncEventHandler? handler = null; - var task = handler.InvokeAsync(null, null!); + Task? task = handler.InvokeAsync(null, null!); Assert.True(task.IsCompleted); } @@ -449,7 +452,7 @@ public void InvokeAsyncOfTNullEverything() { AsyncEventHandler? handler = null; - var task = handler.InvokeAsync(null!, null!); + Task? task = handler.InvokeAsync(null!, null!); Assert.True(task.IsCompleted); } @@ -477,7 +480,7 @@ await Task.Yield(); Assert.Equal(4, ++counter); }; - var task = handlers.InvokeAsync(null, null!); + Task? task = handlers.InvokeAsync(null, null!); task.GetAwaiter().GetResult(); } @@ -498,7 +501,7 @@ await Task.Yield(); Assert.Equal(4, ++counter); }; - var task = handlers.InvokeAsync(null!, null!); + Task? task = handlers.InvokeAsync(null!, null!); task.GetAwaiter().GetResult(); } @@ -515,7 +518,7 @@ await Task.Yield(); throw new ApplicationException("b"); }; - var task = handlers.InvokeAsync(null, null!); + Task? task = handlers.InvokeAsync(null, null!); try { task.GetAwaiter().GetResult(); @@ -542,7 +545,7 @@ await Task.Yield(); throw new ApplicationException("b"); }; - var task = handlers.InvokeAsync(null!, null!); + Task? task = handlers.InvokeAsync(null!, null!); try { task.GetAwaiter().GetResult(); @@ -561,11 +564,11 @@ { var currentTCS = new TaskCompletionSource(); Task latestTask = currentTCS.Task; - var followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None); + Task? followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None); for (int i = 0; i < 3; i++) { - var oldTCS = currentTCS; + TaskCompletionSource? oldTCS = currentTCS; currentTCS = new TaskCompletionSource(); latestTask = currentTCS.Task; oldTCS.SetCanceled(); @@ -581,12 +584,12 @@ var specifiedTaskSource = new TaskCompletionSource(); var currentTCS = new TaskCompletionSource(); Task latestTask = currentTCS.Task; - var followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None, specifiedTaskSource); + Task? followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None, specifiedTaskSource); Assert.Same(specifiedTaskSource.Task, followingTask); for (int i = 0; i < 3; i++) { - var oldTCS = currentTCS; + TaskCompletionSource? oldTCS = currentTCS; currentTCS = new TaskCompletionSource(); latestTask = currentTCS.Task; oldTCS.SetCanceled(); @@ -602,11 +605,11 @@ var currentTCS = new TaskCompletionSource(); Task latestTask = currentTCS.Task; var cts = new CancellationTokenSource(); - var followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, cts.Token); + Task? followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, cts.Token); for (int i = 0; i < 3; i++) { - var oldTCS = currentTCS; + TaskCompletionSource? oldTCS = currentTCS; currentTCS = new TaskCompletionSource(); latestTask = currentTCS.Task; oldTCS.SetCanceled(); @@ -621,11 +624,11 @@ { var currentTCS = new TaskCompletionSource(); Task latestTask = currentTCS.Task; - var followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None); + Task? followingTask = TplExtensions.FollowCancelableTaskToCompletion(() => latestTask, CancellationToken.None); for (int i = 0; i < 3; i++) { - var oldTCS = currentTCS; + TaskCompletionSource? oldTCS = currentTCS; currentTCS = new TaskCompletionSource(); latestTask = currentTCS.Task; oldTCS.SetCanceled(); @@ -829,7 +832,7 @@ this.ExecuteOnDispatcher(async delegate { var tcs = new TaskCompletionSource(); - var timeoutTask = generic + Task? timeoutTask = generic ? TplExtensions.WithTimeout(tcs.Task, TimeSpan.FromMilliseconds(-1)) : TplExtensions.WithTimeout((Task)tcs.Task, TimeSpan.FromMilliseconds(-1)); Assert.False(timeoutTask.IsCompleted); @@ -881,7 +884,7 @@ this.ExecuteOnDispatcher(delegate { var tcs = new TaskCompletionSource(); - var timeoutTask = tcs.Task.WithTimeout(TimeSpan.FromDays(1)); + Task? timeoutTask = tcs.Task.WithTimeout(TimeSpan.FromDays(1)); Assert.False(timeoutTask.IsCompleted); tcs.SetResult("success"); Assert.Same(tcs.Task.Result, timeoutTask.Result); @@ -917,7 +920,7 @@ invoked++; return Task.CompletedTask; }; - var task = handler.InvokeAsync(sender, args!); + Task? task = handler.InvokeAsync(sender, args!); Assert.True(task.IsCompleted); Assert.Equal(1, invoked); } @@ -932,7 +935,7 @@ invoked++; return Task.CompletedTask; }; - var task = handler.InvokeAsync(sender!, args!); + Task? task = handler.InvokeAsync(sender!, args!); Assert.True(task.IsCompleted); Assert.Equal(1, invoked); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs similarity index 78% rename from src/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs index 78f280a9..2be467b5 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/ValidityTests.cs @@ -1,4 +1,7 @@ -namespace Microsoft.VisualStudio.Threading.Tests +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.Threading.Tests { using System; using System.Collections.Generic; diff --git a/src/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs similarity index 96% rename from src/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs rename to test/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs index 79803f96..63a2127a 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/WeakKeyDictionaryTests.cs @@ -1,8 +1,5 @@ -/******************************************************** -* * -* © Copyright (C) Microsoft. All rights reserved. * -* * -*********************************************************/ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Threading.Tests { diff --git a/tools/Check-DotNetRuntime.ps1 b/tools/Check-DotNetRuntime.ps1 new file mode 100644 index 00000000..9d012109 --- /dev/null +++ b/tools/Check-DotNetRuntime.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Checks whether a given .NET Core runtime is installed. +#> +[CmdletBinding()] +Param ( + [Parameter()] + [ValidateSet('Microsoft.AspNetCore.App','Microsoft.NETCore.App')] + [string]$Runtime='Microsoft.NETCore.App', + [Parameter(Mandatory=$true)] + [Version]$Version +) + +$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue +if (!$dotnet) { + # Nothing is installed. + Write-Output $false + exit 1 +} + +Function IsVersionMatch { + Param( + [Parameter()] + $actualVersion + ) + return $actualVersion -and + $Version.Major -eq $actualVersion.Major -and + $Version.Minor -eq $actualVersion.Minor -and + (($Version.Build -eq -1) -or ($Version.Build -eq $actualVersion.Build)) -and + (($Version.Revision -eq -1) -or ($Version.Revision -eq $actualVersion.Revision)) +} + +$installedRuntimes = dotnet --list-runtimes |? { $_.Split()[0] -ieq $Runtime } |% { $v = $null; [Version]::tryparse($_.Split()[1], [ref] $v); $v } +$matchingRuntimes = $installedRuntimes |? { IsVersionMatch -actualVersion $_ } +if (!$matchingRuntimes) { + Write-Output $false + exit 1 +} + +Write-Output $true +exit 0 diff --git a/tools/Check-DotNetSdk.ps1 b/tools/Check-DotNetSdk.ps1 new file mode 100644 index 00000000..6c9fa772 --- /dev/null +++ b/tools/Check-DotNetSdk.ps1 @@ -0,0 +1,37 @@ +<# +.SYNOPSIS + Checks whether the .NET Core SDK required by this repo is installed. +#> +[CmdletBinding()] +Param ( +) + +$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue +if (!$dotnet) { + # Nothing is installed. + Write-Output $false + exit 1 +} + +# We need to set the current directory so dotnet considers the SDK required by our global.json file. +Push-Location "$PSScriptRoot\.." +try { + dotnet -h 2>&1 | Out-Null + if (($LASTEXITCODE -eq 129) -or # On Linux + ($LASTEXITCODE -eq -2147450751) # On Windows + ) { + # These exit codes indicate no matching SDK exists. + Write-Output $false + exit 2 + } + + # The required SDK is already installed! + Write-Output $true + exit 0 +} catch { + # 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. + Write-Output $false + exit 3 +} finally { + Pop-Location +} diff --git a/tools/Install-DotNetSdk.ps1 b/tools/Install-DotNetSdk.ps1 index a5d52eed..ec66c949 100644 --- a/tools/Install-DotNetSdk.ps1 +++ b/tools/Install-DotNetSdk.ps1 @@ -1,18 +1,20 @@ +#!/usr/bin/env pwsh + <# .SYNOPSIS -Installs the .NET SDK specified in the global.json file at the root of this repository, -along with supporting .NET Core runtimes used for testing. + Installs the .NET SDK specified in the global.json file at the root of this repository, + along with supporting .NET Core runtimes used for testing. .DESCRIPTION -This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location, -unless `-InstallLocality machine` is specified. + This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location, + unless `-InstallLocality machine` is specified. .PARAMETER InstallLocality -A value indicating whether dependencies should be installed locally to the repo or at a per-user location. -Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. -Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. -Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. -When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. -Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. -Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. + A value indicating whether dependencies should be installed locally to the repo or at a per-user location. + Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. + Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. + Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. + When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. + Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. + Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. #> [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] Param ( @@ -21,7 +23,7 @@ Param ( ) $DotNetInstallScriptRoot = "$PSScriptRoot/../obj/tools" -if (!(Test-Path $DotNetInstallScriptRoot)) { New-Item -ItemType Directory -Path $DotNetInstallScriptRoot | Out-Null } +if (!(Test-Path $DotNetInstallScriptRoot)) { New-Item -ItemType Directory -Path $DotNetInstallScriptRoot -WhatIf:$false | Out-Null } $DotNetInstallScriptRoot = Resolve-Path $DotNetInstallScriptRoot # Look up actual required .NET Core SDK version from global.json @@ -29,7 +31,7 @@ $sdkVersion = & "$PSScriptRoot/../azure-pipelines/variables/DotNetSdkVersion.ps1 # Search for all .NET Core runtime versions referenced from MSBuild projects and arrange to install them. $runtimeVersions = @() -Get-ChildItem "$PSScriptRoot\..\src\*.*proj" -Recurse |% { +Get-ChildItem "$PSScriptRoot\..\src\*.*proj","$PSScriptRoot\..\test\*.*proj","$PSScriptRoot\..\Directory.Build.props" -Recurse |% { $projXml = [xml](Get-Content -Path $_) $targetFrameworks = $projXml.Project.PropertyGroup.TargetFramework if (!$targetFrameworks) { @@ -63,7 +65,7 @@ Function Get-InstallerExe($Version, [switch]$Runtime) { # Get the latest/actual version for the specified one if (([Version]$Version).Build -eq -1) { - $versionInfo = -Split (Invoke-WebRequest -Uri "https://dotnetcli.blob.core.windows.net/dotnet/$sdkOrRuntime/$Version/latest.version") + $versionInfo = -Split (Invoke-WebRequest -Uri "https://dotnetcli.blob.core.windows.net/dotnet/$sdkOrRuntime/$Version/latest.version" -UseBasicParsing) $Version = $versionInfo[-1] } @@ -75,31 +77,14 @@ Function Install-DotNet($Version, [switch]$Runtime) { Write-Host "Downloading .NET Core $sdkSubstring$Version..." $Installer = Get-InstallerExe -Version $Version -Runtime:$Runtime Write-Host "Installing .NET Core $sdkSubstring$Version..." - cmd /c start /wait $Installer /install /quiet - if ($LASTEXITCODE -ne 0) { + cmd /c start /wait $Installer /install /passive /norestart + if ($LASTEXITCODE -eq 3010) { + Write-Verbose "Restart required" + } elseif ($LASTEXITCODE -ne 0) { throw "Failure to install .NET Core SDK" } } -if ($InstallLocality -eq 'machine') { - if ($IsMacOS -or $IsLinux) { - Write-Error "Installing the .NET Core SDK or runtime at a machine-wide location is only supported by this script on Windows." - exit 1 - } - - if ($PSCmdlet.ShouldProcess(".NET Core SDK $sdkVersion", "Install")) { - Install-DotNet -Version $sdkVersion - } - - $runtimeVersions |% { - if ($PSCmdlet.ShouldProcess(".NET Core runtime $_", "Install")) { - Install-DotNet -Version $_ -Runtime - } - } - - return -} - $switches = @( '-Architecture','x64' ) @@ -108,7 +93,31 @@ $envVars = @{ 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' = 'true'; } -if ($InstallLocality -eq 'repo') { +if ($InstallLocality -eq 'machine') { + if ($IsMacOS -or $IsLinux) { + $DotNetInstallDir = '/usr/share/dotnet' + } else { + $restartRequired = $false + if ($PSCmdlet.ShouldProcess(".NET Core SDK $sdkVersion", "Install")) { + Install-DotNet -Version $sdkVersion + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + + $runtimeVersions | Get-Unique |% { + if ($PSCmdlet.ShouldProcess(".NET Core runtime $_", "Install")) { + Install-DotNet -Version $_ -Runtime + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + } + + if ($restartRequired) { + Write-Host -ForegroundColor Yellow "System restart required" + Exit 3010 + } + + return + } +} elseif ($InstallLocality -eq 'repo') { $DotNetInstallDir = "$DotNetInstallScriptRoot/.dotnet" } elseif ($env:AGENT_TOOLSDIRECTORY) { $DotNetInstallDir = "$env:AGENT_TOOLSDIRECTORY/dotnet" @@ -133,13 +142,16 @@ if ($IsMacOS -or $IsLinux) { } if (-not (Test-Path $DotNetInstallScriptPath)) { - Invoke-WebRequest -Uri $DownloadUri -OutFile $DotNetInstallScriptPath + Invoke-WebRequest -Uri $DownloadUri -OutFile $DotNetInstallScriptPath -UseBasicParsing if ($IsMacOS -or $IsLinux) { chmod +x $DotNetInstallScriptPath } } +$anythingInstalled = $false + if ($PSCmdlet.ShouldProcess(".NET Core SDK $sdkVersion", "Install")) { + $anythingInstalled = $true Invoke-Expression -Command "$DotNetInstallScriptPath -Version $sdkVersion $switches" } else { Invoke-Expression -Command "$DotNetInstallScriptPath -Version $sdkVersion $switches -DryRun" @@ -149,6 +161,7 @@ $switches += '-Runtime','dotnet' $runtimeVersions | Get-Unique |% { if ($PSCmdlet.ShouldProcess(".NET Core runtime $_", "Install")) { + $anythingInstalled = $true Invoke-Expression -Command "$DotNetInstallScriptPath -Channel $_ $switches" } else { Invoke-Expression -Command "$DotNetInstallScriptPath -Channel $_ $switches -DryRun" @@ -156,41 +169,9 @@ $runtimeVersions | Get-Unique |% { } if ($PSCmdlet.ShouldProcess("Set DOTNET environment variables to discover these installed runtimes?")) { - if ($env:TF_BUILD) { - Write-Host "Azure Pipelines detected. Logging commands will be used to propagate environment variables and prepend path." - } - - if ($IsMacOS -or $IsLinux) { - $envVars['PATH'] = "${DotNetInstallDir}:$env:PATH" - } else { - $envVars['PATH'] = "$DotNetInstallDir;$env:PATH" - } - - $envVars.GetEnumerator() |% { - Set-Item -Path env:$($_.Key) -Value $_.Value - - # If we're running in Azure Pipelines, set these environment variables - if ($env:TF_BUILD) { - Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" - } - } - - if ($env:TF_BUILD) { - Write-Host "##vso[task.prependpath]$DotNetInstallDir" - } + & "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars -PrependPath $DotNetInstallDir | Out-Null } -if ($env:PS1UnderCmd -eq '1') { - Write-Warning "Environment variable changes will be lost because you're running under cmd.exe. Run these commands manually:" - $envVars.GetEnumerator() |% { - if ($_.Key -eq 'PATH') { - # Special case this one for readability - Write-Host "SET PATH=$DotNetInstallDir;%PATH%" - } else { - Write-Host "SET $($_.Key)=$($_.Value)" - } - } -} else { - Write-Host "Environment variables set:" -ForegroundColor Blue - $envVars +if ($anythingInstalled -and ($InstallLocality -ne 'machine') -and !$env:TF_BUILD -and !$env:GITHUB_ACTIONS) { + Write-Warning ".NET Core runtimes or SDKs were installed to a non-machine location. Perform your builds or open Visual Studio from this same environment in order for tools to discover the location of these dependencies." } diff --git a/tools/Install-NuGetCredProvider.ps1 b/tools/Install-NuGetCredProvider.ps1 index ba70f2bd..6d310034 100644 --- a/tools/Install-NuGetCredProvider.ps1 +++ b/tools/Install-NuGetCredProvider.ps1 @@ -1,18 +1,26 @@ +#!/usr/bin/env pwsh + <# .SYNOPSIS Downloads and installs the Microsoft Artifacts Credential Provider from https://github.com/microsoft/artifacts-credprovider to assist in authenticating to Azure Artifact feeds in interactive development or unattended build agents. +.PARAMETER Force + Forces install of the CredProvider plugin even if one already exists. This is useful to upgrade an older version. .PARAMETER AccessToken An optional access token for authenticating to Azure Artifacts authenticated feeds. #> [CmdletBinding()] Param ( + [Parameter()] + [switch]$Force, [Parameter()] [string]$AccessToken ) +$envVars = @{} + $toolsPath = & "$PSScriptRoot\..\azure-pipelines\Get-TempToolsPath.ps1" if ($IsMacOS -or $IsLinux) { @@ -35,28 +43,34 @@ if ($IsMacOS -or $IsLinux) { chmod u+x $installerScript } -& $installerScript +& $installerScript -Force:$Force if ($AccessToken) { $endpoints = @() - $nugetConfig = [xml](Get-Content -Path "$PSScriptRoot\..\nuget.config") + $endpointURIs = @() + Get-ChildItem "$PSScriptRoot\..\nuget.config" -Recurse |% { + $nugetConfig = [xml](Get-Content -Path $_) - $nugetConfig.configuration.packageSources.add |? { ($_.value -match '^https://pkgs\.dev\.azure\.com/') -or ($_.value -match '^https://[\w\-]+\.pkgs\.visualstudio\.com/') } |% { - $endpoint = New-Object -TypeName PSObject - Add-Member -InputObject $endpoint -MemberType NoteProperty -Name endpoint -Value $_.value - Add-Member -InputObject $endpoint -MemberType NoteProperty -Name username -Value ado - Add-Member -InputObject $endpoint -MemberType NoteProperty -Name password -Value $AccessToken - $endpoints += $endpoint + $nugetConfig.configuration.packageSources.add |? { ($_.value -match '^https://pkgs\.dev\.azure\.com/') -or ($_.value -match '^https://[\w\-]+\.pkgs\.visualstudio\.com/') } |% { + if ($endpointURIs -notcontains $_.Value) { + $endpointURIs += $_.Value + $endpoint = New-Object -TypeName PSObject + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name endpoint -Value $_.value + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name username -Value ado + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name password -Value $AccessToken + $endpoints += $endpoint + } + } } $auth = New-Object -TypeName PSObject Add-Member -InputObject $auth -MemberType NoteProperty -Name endpointCredentials -Value $endpoints $authJson = ConvertTo-Json -InputObject $auth - $envVars = @{ + $envVars += @{ 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'=$authJson; } - - & "$PSScriptRoot\..\azure-pipelines\Set-EnvVars.ps1" -Variables $envVars | Out-Null } + +& "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars | Out-Null diff --git a/tools/Set-EnvVars.ps1 b/tools/Set-EnvVars.ps1 new file mode 100644 index 00000000..bde2e343 --- /dev/null +++ b/tools/Set-EnvVars.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS + Set environment variables in the environment. + Azure Pipeline and CMD environments are considered. +.PARAMETER Variables + A hashtable of variables to be set. +.PARAMETER PrependPath + A set of paths to prepend to the PATH environment variable. +.OUTPUTS + A boolean indicating whether the environment variables can be expected to propagate to the caller's environment. +.DESCRIPTION + The CmdEnvScriptPath environment variable may be optionally set to a path to a cmd shell script to be created (or appended to if it already exists) that will set the environment variables in cmd.exe that are set within the PowerShell environment. + This is used by init.cmd in order to reapply any new environment variables to the parent cmd.exe process that were set in the powershell child process. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +Param( + [Parameter(Mandatory=$true, Position=1)] + $Variables, + [string[]]$PrependPath +) + +if ($Variables.Count -eq 0) { + return $true +} + +$cmdInstructions = !$env:TF_BUILD -and !$env:GITHUB_ACTIONS -and !$env:CmdEnvScriptPath -and ($env:PS1UnderCmd -eq '1') +if ($cmdInstructions) { + Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe" + Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue +} else { + Write-Host "Environment variables set:" -ForegroundColor Blue + Write-Host ($Variables | Out-String) + if ($PrependPath) { + Write-Host "Paths prepended to PATH: $PrependPath" + } +} + +if ($env:TF_BUILD) { + Write-Host "Azure Pipelines detected. Logging commands will be used to propagate environment variables and prepend path." +} + +if ($env:GITHUB_ACTIONS) { + Write-Host "GitHub Actions detected. Logging commands will be used to propagate environment variables and prepend path." +} + +$CmdEnvScript = '' +$Variables.GetEnumerator() |% { + Set-Item -Path env:$($_.Key) -Value $_.Value + + # If we're running in a cloud CI, set these environment variables so they propagate. + if ($env:TF_BUILD) { + Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" + } + if ($env:GITHUB_ACTIONS) { + Write-Host "::set-env name=$($_.Key)::$($_.Value)" + } + + if ($cmdInstructions) { + Write-Host "SET $($_.Key)=$($_.Value)" + } + + $CmdEnvScript += "SET $($_.Key)=$($_.Value)`r`n" +} + +$pathDelimiter = ';' +if ($IsMacOS -or $IsLinux) { + $pathDelimiter = ':' +} + +if ($PrependPath) { + $PrependPath |% { + $newPathValue = "$_$pathDelimiter$env:PATH" + Set-Item -Path env:PATH -Value $newPathValue + if ($cmdInstructions) { + Write-Host "SET PATH=$newPathValue" + } + + if ($env:TF_BUILD) { + Write-Host "##vso[task.prependpath]$_" + } + if ($env:GITHUB_ACTIONS) { + Write-Host "::add-path::$_" + } + + $CmdEnvScript += "SET PATH=$_$pathDelimiter%PATH%" + } +} + +if ($env:CmdEnvScriptPath) { + if (Test-Path $env:CmdEnvScriptPath) { + $CmdEnvScript = (Get-Content -Path $env:CmdEnvScriptPath) + $CmdEnvScript + } + + Set-Content -Path $env:CmdEnvScriptPath -Value $CmdEnvScript +} + +return !$cmdInstructions diff --git a/version.json b/version.json index afb00a7c..6a16bd22 100644 --- a/version.json +++ b/version.json @@ -1,8 +1,8 @@ { - "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", "version": "16.8-alpha", "publicReleaseRefSpec": [ "^refs/heads/master$", "^refs/heads/v\\d+(?:\\.\\d+)?$" ] -} \ No newline at end of file +}