Initial commit.

This commit is contained in:
Dylan R. E. Moonfire 2018-01-18 16:16:01 -06:00
commit f01a18a15a
17 changed files with 2151 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# 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
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8-bom
indent_style = space
tab_width = 4
trim_trailing_whitespace = true

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# 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

261
.gitignore vendored Normal file
View File

@ -0,0 +1,261 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# 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
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# 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
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) Moonfire Games
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25
MfGames.Locking.sln Normal file
View File

@ -0,0 +1,25 @@

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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FED5BE58-8CEB-48E6-82E0-0B80035F4AEA}
EndGlobalSection
EndGlobal

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net452;net461;netstandard1.6;netstandard2.0;netcoreapp2.0</TargetFrameworks>
<PackageId>MfGames.Locking</PackageId>
<PackageVersion>5.0.0</PackageVersion>
<Authors>Dylan R. E. Moonfire</Authors>
<Description>Various locking patterns for multi-threaded code.</Description>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Copyright>Copyright (c) Moonfire Games. MIT license.</Copyright>
<PackageTags>lock; locking</PackageTags>
<PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\Debug\netcoreapp2.0\MfGames.Locking.xml</DocumentationFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile />
</PropertyGroup>
<ItemGroup>
<None Remove="stylecop.json" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>compile; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
// <copyright file="NestableReadLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Defines a ReaderWriterLockSlim read-only lock.
/// </summary>
public class NestableReadLock : IDisposable
{
private readonly bool lockAcquired;
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="NestableReadLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public NestableReadLock(ReaderWriterLockSlim readerWriterLockSlim)
{
// Keep track of the lock since we'll need it to release the lock.
this.readerWriterLockSlim = readerWriterLockSlim;
// If we already have a read or write lock, we don't do anything.
if (readerWriterLockSlim.IsReadLockHeld
|| readerWriterLockSlim.IsUpgradeableReadLockHeld
|| readerWriterLockSlim.IsWriteLockHeld)
{
this.lockAcquired = false;
}
else
{
readerWriterLockSlim.EnterReadLock();
this.lockAcquired = true;
}
}
/// <inheritdoc />
public void Dispose()
{
if (this.lockAcquired)
{
this.readerWriterLockSlim.ExitReadLock();
}
}
}
}

View File

@ -0,0 +1,52 @@
// <copyright file="NestableUpgradableReadLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Defines a ReaderWriterLockSlim upgradable read lock.
/// </summary>
public class NestableUpgradableReadLock : IDisposable
{
private readonly bool lockAcquired;
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="NestableUpgradableReadLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public NestableUpgradableReadLock(ReaderWriterLockSlim readerWriterLockSlim)
{
// Keep track of the lock since we'll need it to release the lock.
this.readerWriterLockSlim = readerWriterLockSlim;
// If we already have a read or write lock, we don't do anything.
if (readerWriterLockSlim.IsUpgradeableReadLockHeld
|| readerWriterLockSlim.IsWriteLockHeld)
{
this.lockAcquired = false;
}
else
{
readerWriterLockSlim.EnterUpgradeableReadLock();
this.lockAcquired = true;
}
}
/// <inheritdoc />
public void Dispose()
{
if (this.lockAcquired)
{
this.readerWriterLockSlim.ExitUpgradeableReadLock();
}
}
}
}

View File

@ -0,0 +1,51 @@
// <copyright file="NestableWriteLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Defines a ReaderWriterLockSlim write lock.
/// </summary>
public class NestableWriteLock : IDisposable
{
private readonly bool lockAcquired;
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="NestableWriteLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public NestableWriteLock(ReaderWriterLockSlim readerWriterLockSlim)
{
// Keep track of the lock since we'll need it to release the lock.
this.readerWriterLockSlim = readerWriterLockSlim;
// If we already have a read or write lock, we don't do anything.
if (readerWriterLockSlim.IsWriteLockHeld)
{
this.lockAcquired = false;
}
else
{
readerWriterLockSlim.EnterWriteLock();
this.lockAcquired = true;
}
}
/// <inheritdoc />
public void Dispose()
{
if (this.lockAcquired)
{
this.readerWriterLockSlim.ExitWriteLock();
}
}
}
}

View File

@ -0,0 +1,36 @@
// <copyright file="ReadLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <inheritdoc />
/// <summary>
/// Defines a ReaderWriterLockSlim read-only lock.
/// </summary>
public class ReadLock : IDisposable
{
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="ReadLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public ReadLock(ReaderWriterLockSlim readerWriterLockSlim)
{
this.readerWriterLockSlim = readerWriterLockSlim;
readerWriterLockSlim.EnterReadLock();
}
/// <inheritdoc />
public void Dispose()
{
this.readerWriterLockSlim.ExitReadLock();
}
}
}

View File

@ -0,0 +1,111 @@
// <copyright file="TryGetCreate.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Implements the basic pattern for getting an item from a cache using
/// the ReaderWriterLockSlim class. This attempts to get it using a read-only
/// lock. If that fails, it gets an upgradable lock, tries again, and if it
/// still can't find it, upgrades the lock to a write lock to create it.
/// </summary>
public static class TryGetCreate
{
/// <summary>
/// Invokes the try/get/create pattern used a condition to test for it
/// and a constructor function.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
/// <param name="conditionHandler">The condition handler.</param>
/// <param name="createHandler">The create handler.</param>
public static void Invoke(
ReaderWriterLockSlim readerWriterLockSlim,
Func<bool> conditionHandler,
Action createHandler)
{
using (new ReadLock(readerWriterLockSlim))
{
// Verify that the condition for creating it is false.
if (!conditionHandler())
{
return;
}
}
// We failed to get the lock using the read-only. We create an upgradable lock
// and try again since it may have been created with a race condition when the
// last lock was released and this one was acquired.
using (new UpgradableLock(readerWriterLockSlim))
{
// Verify that the condition for creating it is false.
if (!conditionHandler())
{
return;
}
// We failed to get it in the lock. Upgrade the lock to a write and create it.
using (new WriteLock(readerWriterLockSlim))
{
createHandler();
}
}
}
/// <summary>
/// Invokes the try/get/create pattern using a tryget retrieval and a
/// creator handler.
/// </summary>
/// <typeparam name="TInput">The type of the input.</typeparam>
/// <typeparam name="TOutput">The type of the output.</typeparam>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
/// <param name="input">The input.</param>
/// <param name="tryGetHandler">The try get handler.</param>
/// <param name="createHandler">The create handler.</param>
/// <returns>The requested output object.</returns>
public static TOutput Invoke<TInput, TOutput>(
ReaderWriterLockSlim readerWriterLockSlim,
TInput input,
TryGetHandler<TInput, TOutput> tryGetHandler,
Func<TInput, TOutput> createHandler)
{
// First attempt to get the item using a read-only lock.
TOutput output;
using (new ReadLock(readerWriterLockSlim))
{
// Try to get the item using the try/get handler.
if (tryGetHandler(input, out output))
{
// We successful got the item in the read-only cache, so just return it.
return output;
}
}
// We failed to get the lock using the read-only. We create an upgradable lock
// and try again since it may have been created with a race condition when the
// last lock was released and this one was acquired.
using (new UpgradableLock(readerWriterLockSlim))
{
// Try to get the item using the try/get handler.
if (tryGetHandler(input, out output))
{
// We successful got the item in this lock, so return it without
// upgrading the lock.
return output;
}
// We failed to get it in the lock. Upgrade the lock to a write and create it.
using (new WriteLock(readerWriterLockSlim))
{
return createHandler(input);
}
}
}
}
}

View File

@ -0,0 +1,20 @@
// <copyright file="TryGetHandler.cs" 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>
namespace MfGames.Locking
{
/// <summary>
/// Defines the common try/get handler to retrieve an item of a given type.
/// </summary>
/// <typeparam name="TInput">The type of the input or lookup key.</typeparam>
/// <typeparam name="TOutput">The type of the output.</typeparam>
/// <param name="input">The input value to search for.</param>
/// <param name="output">The resulting output value.</param>
/// <returns>True if the attempt was successful, otherwise false.</returns>
public delegate bool TryGetHandler<in TInput, TOutput>(
TInput input,
out TOutput output);
}

View File

@ -0,0 +1,37 @@
// <copyright file="UpgradableLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Defines a ReaderWriterLockSlim read-only lock.
/// </summary>
public class UpgradableLock : IDisposable
{
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="UpgradableLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public UpgradableLock(ReaderWriterLockSlim readerWriterLockSlim)
{
this.readerWriterLockSlim = readerWriterLockSlim;
readerWriterLockSlim.EnterUpgradeableReadLock();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.readerWriterLockSlim.ExitUpgradeableReadLock();
}
}
}

View File

@ -0,0 +1,37 @@
// <copyright file="WriteLock.cs" 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>
namespace MfGames.Locking
{
using System;
using System.Threading;
/// <summary>
/// Defines a ReaderWriterLockSlim read-only lock.
/// </summary>
public class WriteLock : IDisposable
{
private readonly ReaderWriterLockSlim readerWriterLockSlim;
/// <summary>
/// Initializes a new instance of the <see cref="WriteLock" /> class.
/// </summary>
/// <param name="readerWriterLockSlim">The reader writer lock slim.</param>
public WriteLock(ReaderWriterLockSlim readerWriterLockSlim)
{
this.readerWriterLockSlim = readerWriterLockSlim;
readerWriterLockSlim.EnterWriteLock();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.readerWriterLockSlim.ExitWriteLock();
}
}
}

View File

@ -0,0 +1,11 @@
{
"$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"
}
}
}

15
README.md Normal file
View File

@ -0,0 +1,15 @@
MfGames.Locking CIL
===================
This a small collection of classes that provide some simplification while working with locking in C# applications.
```
//using MfGames.Locking;
ReaderWriterLockSlim locker;
using (new ReadLock(locker))
{
// Do something.
}
```