diff --git a/.editorconfig b/.editorconfig index c90eff1..6d18247 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,16 +1,27 @@ # EditorConfig is awesome: http://EditorConfig.org -# -# https://visualstudiogallery.msdn.microsoft.com/c8bccfe2-650c-4b42-bc5c-845e21f96328 -# Or install via Extensions inside Visual Studio. # top-most EditorConfig file root = true -# Unix-style newlines with a newline ending every file [*] +charset = utf-8 end_of_line = lf -insert_final_newline = true -charset = utf-8-bom +indent_brace_style = K&R +indent_size = 4 indent_style = space +insert_final_newline = true +max_line_length = 120 tab_width = 4 trim_trailing_whitespace = true +curly_bracket_next_line = true + +[*.{js,ts}] +quote_type = double + +[*.json] +indent_size = 2 +tab_width = 2 +indent_style = space + +[*.yaml] +indent_style = space diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c42..0000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 3c4efe2..f5dfa15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,261 +1,15 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. +launchSettings.json -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* *~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs +*.user -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ +NaNoGenMo.* -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider +obj/ +[Bb]in/ +.vs/ +.vscode/ .idea/ -*.sln.iml +_ReSharper.Caches/ -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc \ No newline at end of file +.author-intrusion diff --git a/MfGames.Locking.sln b/MfGames.Locking.sln index 264106d..1bc0268 100644 --- a/MfGames.Locking.sln +++ b/MfGames.Locking.sln @@ -1,25 +1,57 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2024 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Locking", "MfGames.Locking\MfGames.Locking.csproj", "{51E00BEF-AAF1-4D45-BF82-5DCD7D5EA7FC}" +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EE3B4148-AC01-4E30-93AB-AA6EC2201555}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MfGames.Locking", "src\MfGames.Locking\MfGames.Locking.csproj", "{016D2590-46B8-4776-A7F9-1B0586920932}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Locking.Tests", "src\MfGames.Locking.Tests\MfGames.Locking.Tests.csproj", "{50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {51E00BEF-AAF1-4D45-BF82-5DCD7D5EA7FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {51E00BEF-AAF1-4D45-BF82-5DCD7D5EA7FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51E00BEF-AAF1-4D45-BF82-5DCD7D5EA7FC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51E00BEF-AAF1-4D45-BF82-5DCD7D5EA7FC}.Release|Any CPU.Build.0 = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|Any CPU.Build.0 = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|x64.ActiveCfg = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|x64.Build.0 = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|x86.ActiveCfg = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Debug|x86.Build.0 = Debug|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|Any CPU.ActiveCfg = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|Any CPU.Build.0 = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|x64.ActiveCfg = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|x64.Build.0 = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|x86.ActiveCfg = Release|Any CPU + {016D2590-46B8-4776-A7F9-1B0586920932}.Release|x86.Build.0 = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|x64.ActiveCfg = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|x64.Build.0 = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|x86.ActiveCfg = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Debug|x86.Build.0 = Debug|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|Any CPU.Build.0 = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|x64.ActiveCfg = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|x64.Build.0 = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|x86.ActiveCfg = Release|Any CPU + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {016D2590-46B8-4776-A7F9-1B0586920932} = {EE3B4148-AC01-4E30-93AB-AA6EC2201555} + {50475AFD-8D3A-4AB2-9B16-A2FC3D6AE05C} = {EE3B4148-AC01-4E30-93AB-AA6EC2201555} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FED5BE58-8CEB-48E6-82E0-0B80035F4AEA} + SolutionGuid = {78D75C86-DAB1-43D2-AD2D-B0D57EDF2ED3} EndGlobalSection EndGlobal diff --git a/MfGames.Locking.sln.DotSettings b/MfGames.Locking.sln.DotSettings index c62cd45..59dfc40 100644 --- a/MfGames.Locking.sln.DotSettings +++ b/MfGames.Locking.sln.DotSettings @@ -37,11 +37,12 @@ DoHide DoHide HINT - <?xml version="1.0" encoding="utf-16"?><Profile name="Full WEI Cleanup"><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_BOTH</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName>Namespaces</RegionName></CSOptimizeUsings><CSUseAutoProperty>True</CSUseAutoProperty><CSArrangeQualifiers>True</CSArrangeQualifiers><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="False" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSUpdateFileHeader>True</CSUpdateFileHeader><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><HtmlReformatCode>True</HtmlReformatCode></Profile> + <?xml version="1.0" encoding="utf-16"?><Profile name="Project"><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_BOTH</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName>Namespaces</RegionName></CSOptimizeUsings><CSUseAutoProperty>True</CSUseAutoProperty><CSArrangeQualifiers>True</CSArrangeQualifiers><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="False" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSUpdateFileHeader>True</CSUpdateFileHeader><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><HtmlReformatCode>True</HtmlReformatCode></Profile> <?xml version="1.0" encoding="utf-16"?><Profile name="Remove Redundant Qualifiers"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>False</CSUseAutoProperty><CSMakeFieldReadonly>False</CSMakeFieldReadonly><CSUseVar><BehavourStyle>DISABLED</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSUpdateFileHeader>False</CSUpdateFileHeader><VBOptimizeImports>False</VBOptimizeImports><VBShortenReferences>False</VBShortenReferences><CSOptimizeUsings><OptimizeUsings>False</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName /></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><VBReformatCode>False</VBReformatCode><CSReformatCode>False</CSReformatCode><CSReorderTypeMembers>False</CSReorderTypeMembers></Profile> Full Cleanup - Full WEI Cleanup + Project OPTIMAL_FILL + LF True False True @@ -67,7 +68,7 @@ 1 1 - 1 + NEXT_LINE SEPARATE ALWAYS_ADD @@ -522,13 +523,13 @@ II.2.12 <HandlesEvent /> CustomLayout True False + True UseExplicitType UseVarWhenEvident UseVarWhenEvident + True <copyright file="$FILENAME$" company="Moonfire Games"> -Copyright (c) Moonfire Games. -This code is licensed under the MIT license. -See LICENSE.md in the source or https://opensource.org/licenses/MIT +Copyright (c) Moonfire Games. MIT License. </copyright> False @@ -640,6 +641,7 @@ See LICENSE.md in the source or https://opensource.org/licenses/MIT <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + C:\Users\rc04727\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_ede7cb44\SolutionCaches LIVE_MONITOR LIVE_MONITOR DO_NOTHING @@ -671,6 +673,8 @@ See LICENSE.md in the source or https://opensource.org/licenses/MIT True Always False + <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="MfGames.Locking.Tests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data> + ForWarningsOrErrors tag 10 ( ) @@ -1300,4 +1304,4 @@ using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionStri 0 True 2.0 - InCSharpStatement \ No newline at end of file + InCSharpStatement diff --git a/MfGames.Locking/MfGames.Locking.csproj b/MfGames.Locking/MfGames.Locking.csproj deleted file mode 100644 index c3a4a82..0000000 --- a/MfGames.Locking/MfGames.Locking.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - net452;net461;netstandard1.6;netstandard2.0;netcoreapp2.0 - MfGames.Locking - 5.0.0 - Dylan R. E. Moonfire - Various locking patterns for multi-threaded code. - false - Copyright (c) Moonfire Games. MIT license. - lock; locking - https://opensource.org/licenses/MIT - - - - bin\Debug\netcoreapp2.0\MfGames.Locking.xml - Off - - - - - - - - - - - - - - - - - all - compile; build; native; contentfiles; analyzers - - - - diff --git a/MfGames.Locking/stylecop.json b/MfGames.Locking/stylecop.json deleted file mode 100644 index 68e837c..0000000 --- a/MfGames.Locking/stylecop.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": - "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "Moonfire Games", - "copyrightText": - "Copyright (c) {companyName}.\nThis code is licensed under the MIT license.\nSee LICENSE.md in the source or https://opensource.org/licenses/MIT" - } - } -} diff --git a/src/MfGames.Locking.Tests/LockTests.cs b/src/MfGames.Locking.Tests/LockTests.cs new file mode 100644 index 0000000..75b883c --- /dev/null +++ b/src/MfGames.Locking.Tests/LockTests.cs @@ -0,0 +1,209 @@ +// +// Copyright (c) Moonfire Games. MIT License. +// + +namespace MfGames.Locking.Tests +{ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using Xunit; + using Xunit.Abstractions; + + public class LockTests + { + public static readonly TimeSpan ProcessTime = TimeSpan.FromMilliseconds(50); + + private readonly List events; + + private readonly ReaderWriterLockSlim locker; + + private readonly ITestOutputHelper output; + + private readonly DateTime start; + + public LockTests(ITestOutputHelper output) + { + this.output = output; + this.start = DateTime.UtcNow; + this.locker = new ReaderWriterLockSlim(); + this.events = new List(); + } + + private TimeSpan Elapsed => DateTime.UtcNow - this.start; + + [Fact] + public void BasicDataGatheringWorks() + { + this.Report(ProcessTime, 1); + this.Verify(); + } + + [Fact] + public void ReadBlocksWrite() + { + Task.WaitAll( + Task.Run(() => this.ReportInReadLock(ProcessTime * 0, ProcessTime * 3, 1)), + Task.Run(() => this.ReportInWriteLock(ProcessTime * 1, ProcessTime * 1, 2))); + + this.Verify(); + } + + [Fact] + public void ReadsDoNotBlockReads() + { + Task.WaitAll( + Task.Run(() => this.ReportInReadLock(ProcessTime, ProcessTime * 5, 3)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 1)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 2))); + + this.Verify(); + } + + [Fact] + public void ThreadedDataGatheringWorks() + { + Task.WaitAll( + Task.Run(() => this.ReportInReadLock(ProcessTime, ProcessTime, 1))); + + this.Verify(); + } + + [Fact] + public void UpgradableBlocksUpgradable() + { + Task.WaitAll( + Task.Run(() => this.ReportInUpgradableLock(ProcessTime, ProcessTime * 5, 1)), + Task.Run(() => this.ReportInUpgradableLock(ProcessTime * 3, ProcessTime, 2)), + Task.Run(() => this.ReportInUpgradableLock(ProcessTime * 2, ProcessTime * 2, 3))); + + this.Verify(); + } + + [Fact] + public void UpgradableDoesNotBlockReads() + { + Task.WaitAll( + Task.Run(() => this.ReportInUpgradableLock(ProcessTime, ProcessTime * 5, 3)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 1)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 2))); + + this.Verify(); + } + + [Fact] + public void WriteBlockRead() + { + Task.WaitAll( + Task.Run(() => this.ReportInWriteLock(ProcessTime, ProcessTime * 3, 1)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 2))); + + this.Verify(); + } + + [Fact] + public void WriteBlockReads() + { + Task.WaitAll( + Task.Run(() => this.ReportInWriteLock(ProcessTime, ProcessTime * 5, 1)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime, 2)), + Task.Run(() => this.ReportInReadLock(ProcessTime * 2, ProcessTime * 2, 3))); + + this.Verify(); + } + + private void AddEvent(int sequence) + { + // This is a monitor lock on the slim, just to give us a second lock. + lock (this.locker) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => adding sequence"); + this.events.Add(sequence); + } + } + + /// + /// Waits a short period of time and then injects the sequence into the event list. + /// + private void Report( + TimeSpan processTime, + int sequence) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting process"); + Thread.Sleep(processTime); + this.output.WriteLine(this.Elapsed + ": " + sequence + " => finished process"); + this.AddEvent(sequence); + } + + private void ReportInReadLock( + TimeSpan settleTime, + TimeSpan processTime, + int sequence) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering read lock: " + sequence); + + Thread.Sleep(settleTime); + + this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking read lock: " + sequence); + + using (new ReadLock(this.locker)) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting read lock: " + sequence); + + this.Report(processTime, sequence); + } + } + + private void ReportInUpgradableLock( + TimeSpan settleTime, + TimeSpan processTime, + int sequence) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering upgradable lock: " + sequence); + + Thread.Sleep(settleTime); + + this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking upgradable lock: " + sequence); + + using (new UpgradableLock(this.locker)) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting upgradable lock: " + sequence); + + this.Report(processTime, sequence); + } + } + + private void ReportInWriteLock( + TimeSpan settleTime, + TimeSpan processTime, + int sequence) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => entering write lock: " + sequence); + + Thread.Sleep(settleTime); + + this.output.WriteLine(this.Elapsed + ": " + sequence + " => locking write lock: " + sequence); + + using (new WriteLock(this.locker)) + { + this.output.WriteLine(this.Elapsed + ": " + sequence + " => starting write lock: " + sequence); + + this.Report(processTime, sequence); + } + } + + /// + /// Verifies the sequence of events. + /// + private void Verify() + { + this.output.WriteLine("Final Sequence: " + string.Join(", ", this.events)); + + for (int i = 1; i <= this.events.Count; i++) + { + Assert.Equal(i, this.events[i - 1]); + } + } + } +} diff --git a/src/MfGames.Locking.Tests/MfGames.Locking.Tests.csproj b/src/MfGames.Locking.Tests/MfGames.Locking.Tests.csproj new file mode 100644 index 0000000..4277460 --- /dev/null +++ b/src/MfGames.Locking.Tests/MfGames.Locking.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + diff --git a/src/MfGames.Locking/MfGames.Locking.csproj b/src/MfGames.Locking/MfGames.Locking.csproj new file mode 100644 index 0000000..7080019 --- /dev/null +++ b/src/MfGames.Locking/MfGames.Locking.csproj @@ -0,0 +1,37 @@ + + + + netcoreapp2.1 + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + Dylan Moonfire + Moonfire Games + https://gitlab.com/dmoonfire/mfgames-locking-cil + Git + locking + https://gitlab.com/dmoonfire/mfgames-locking-cil + https://gitlab.com/dmoonfire/mfgames-locking-cil/blob/master/LICENSE.txt + An IDisposable pattern for using ReaderWriterLockSlim. + + + ..\..\bin + ..\..\bin\$(AssemblyName).xml + + + ..\..\bin + ..\..\bin\$(AssemblyName).xml + + + + + + + + + + + + + + diff --git a/MfGames.Locking/NestableReadLock.cs b/src/MfGames.Locking/NestableReadLock.cs similarity index 91% rename from MfGames.Locking/NestableReadLock.cs rename to src/MfGames.Locking/NestableReadLock.cs index 915c5f9..f353b45 100644 --- a/MfGames.Locking/NestableReadLock.cs +++ b/src/MfGames.Locking/NestableReadLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/NestableUpgradableReadLock.cs b/src/MfGames.Locking/NestableUpgradableReadLock.cs similarity index 91% rename from MfGames.Locking/NestableUpgradableReadLock.cs rename to src/MfGames.Locking/NestableUpgradableReadLock.cs index d5e4041..c37287d 100644 --- a/MfGames.Locking/NestableUpgradableReadLock.cs +++ b/src/MfGames.Locking/NestableUpgradableReadLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/NestableWriteLock.cs b/src/MfGames.Locking/NestableWriteLock.cs similarity index 90% rename from MfGames.Locking/NestableWriteLock.cs rename to src/MfGames.Locking/NestableWriteLock.cs index 632dc11..d1701de 100644 --- a/MfGames.Locking/NestableWriteLock.cs +++ b/src/MfGames.Locking/NestableWriteLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/ReadLock.cs b/src/MfGames.Locking/ReadLock.cs similarity index 85% rename from MfGames.Locking/ReadLock.cs rename to src/MfGames.Locking/ReadLock.cs index 9475b6e..995f4ac 100644 --- a/MfGames.Locking/ReadLock.cs +++ b/src/MfGames.Locking/ReadLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/TryGetCreate.cs b/src/MfGames.Locking/TryGetCreate.cs similarity index 96% rename from MfGames.Locking/TryGetCreate.cs rename to src/MfGames.Locking/TryGetCreate.cs index ca0e187..8152b65 100644 --- a/MfGames.Locking/TryGetCreate.cs +++ b/src/MfGames.Locking/TryGetCreate.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/TryGetHandler.cs b/src/MfGames.Locking/TryGetHandler.cs similarity index 82% rename from MfGames.Locking/TryGetHandler.cs rename to src/MfGames.Locking/TryGetHandler.cs index d3f26a9..e6339c5 100644 --- a/MfGames.Locking/TryGetHandler.cs +++ b/src/MfGames.Locking/TryGetHandler.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/UpgradableLock.cs b/src/MfGames.Locking/UpgradableLock.cs similarity index 87% rename from MfGames.Locking/UpgradableLock.cs rename to src/MfGames.Locking/UpgradableLock.cs index e50dc30..1906ae3 100644 --- a/MfGames.Locking/UpgradableLock.cs +++ b/src/MfGames.Locking/UpgradableLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/MfGames.Locking/WriteLock.cs b/src/MfGames.Locking/WriteLock.cs similarity index 87% rename from MfGames.Locking/WriteLock.cs rename to src/MfGames.Locking/WriteLock.cs index fd7d892..ff99346 100644 --- a/MfGames.Locking/WriteLock.cs +++ b/src/MfGames.Locking/WriteLock.cs @@ -1,7 +1,5 @@ // -// Copyright (c) Moonfire Games. -// This code is licensed under the MIT license. -// See LICENSE.md in the source or https://opensource.org/licenses/MIT +// Copyright (c) Moonfire Games. MIT License. // namespace MfGames.Locking diff --git a/src/stylecop.json b/src/stylecop.json new file mode 100644 index 0000000..94dc57b --- /dev/null +++ b/src/stylecop.json @@ -0,0 +1,10 @@ +{ + "$schema": + "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Moonfire Games", + "copyrightText": "Copyright (c) Moonfire Games. MIT License." + } + } +}