diff --git a/.gitignore b/.gitignore
index 2f72fc4..846c8c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,34 +1,79 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
+*.userosscache
*.sln.docstates
-# Build results
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+# Build results
[Dd]ebug/
+[Dd]ebugPublic/
[Rr]elease/
+[Rr]eleases/
x64/
-build/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
[Bb]in/
[Oo]bj/
+[Ll]og/
-# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
-!packages/*/build/
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
*_i.c
*_p.c
+*_h.h
*.ilk
*.meta
*.obj
+*.iobj
*.pch
*.pdb
+*.ipdb
*.pgc
*.pgd
*.rsp
@@ -38,26 +83,40 @@ build/
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
-*.log
+*.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
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
# Guidance Automation Toolkit
*.gpState
@@ -65,6 +124,10 @@ ipch/
# 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*
@@ -72,9 +135,25 @@ _TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
# NCrunch
-*.ncrunch*
+_NCrunch_*
.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
# Installshield output folder
[Ee]xpress/
@@ -93,64 +172,178 @@ DocProject/Help/html
publish/
# Publish Web Output
-*.Publish.xml
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: 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
-# NuGet Packages Directory
-## TODO: If you have NuGet Package Restore enabled, uncomment the next line
-packages/
+# 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/
-# Windows Azure Build Output
-csx
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
*.build.csdef
-# Windows Store app package directory
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
# Others
-sql/
-*.Cache
ClientBin/
-[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
-*.[Pp]ublish.xml
+*.dbproj.schemaview
+*.jfm
*.pfx
*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# 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 ;-)
+# 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
+ServiceFabricBackup/
+*.rptproj.bak
# SQL Server files
-App_Data/*.mdf
-App_Data/*.ldf
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# 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/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+# Azure Stream Analytics local run output
+ASALocalRun/
-#LightSwitch generated files
-GeneratedArtifacts/
-_Pvt_Extensions/
-ModelManifest.xml
+# MSBuild Binary and Structured Log
+*.binlog
-# =========================
-# Windows detritus
-# =========================
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
-# Folder config file
-Desktop.ini
+# Local History for Visual Studio
+.localhistory/
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
+# BeatPulse healthcheck temp database
+healthchecksdb
-# Mac desktop service store files
-.DS_Store
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
\ No newline at end of file
diff --git a/RedLockNet.Abstractions/IRedLock.cs b/RedLockNet.Abstractions/IRedLock.cs
index f939772..413e9f0 100644
--- a/RedLockNet.Abstractions/IRedLock.cs
+++ b/RedLockNet.Abstractions/IRedLock.cs
@@ -33,5 +33,10 @@ public interface IRedLock : IDisposable
/// The number of times the lock has been extended.
///
int ExtendCount { get; }
+
+ ///
+ /// Triggered whenever status property changes.
+ ///
+ event EventHandler OnStatusChanged;
}
}
\ No newline at end of file
diff --git a/RedLockNet.SERedis/Events/RedLockStatusChangedEventArgs.cs b/RedLockNet.SERedis/Events/RedLockStatusChangedEventArgs.cs
new file mode 100644
index 0000000..99555e7
--- /dev/null
+++ b/RedLockNet.SERedis/Events/RedLockStatusChangedEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace RedLockNet.SERedis.Events
+{
+ public class RedLockStatusChangedEventArgs : EventArgs
+ {
+ public T OldValue { get; }
+ public T NewValue { get; }
+
+ public RedLockStatusChangedEventArgs(T oldValue, T newValue)
+ {
+ OldValue = oldValue;
+ NewValue = newValue;
+ }
+ }
+}
diff --git a/RedLockNet.SERedis/RedLock.cs b/RedLockNet.SERedis/RedLock.cs
index 56bb6cd..402c59d 100644
--- a/RedLockNet.SERedis/RedLock.cs
+++ b/RedLockNet.SERedis/RedLock.cs
@@ -7,6 +7,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
+using RedLockNet.SERedis.Events;
using RedLockNet.SERedis.Internal;
using RedLockNet.SERedis.Util;
using StackExchange.Redis;
@@ -26,7 +27,9 @@ public class RedLock : IRedLock
private readonly double clockDriftFactor;
private bool isDisposed;
- private Timer lockKeepaliveTimer;
+ private Timer lockKeepAliveTimer;
+
+ private RedLockStatus status;
private static readonly string UnlockScript = EmbeddedResourceLoader.GetEmbeddedResource("RedLockNet.SERedis.Lua.Unlock.lua");
@@ -37,10 +40,25 @@ public class RedLock : IRedLock
public string Resource { get; }
public string LockId { get; }
public bool IsAcquired => Status == RedLockStatus.Acquired;
- public RedLockStatus Status { get; private set; }
+ public event EventHandler OnStatusChanged;
public RedLockInstanceSummary InstanceSummary { get; private set; }
public int ExtendCount { get; private set; }
+ public RedLockStatus Status
+ {
+ get => status;
+ private set
+ {
+ if (status == value)
+ return;
+
+ var old = status;
+ status = value;
+
+ OnStatusChanged?.Invoke(this, new RedLockStatusChangedEventArgs(old, status));
+ }
+ }
+
private readonly TimeSpan expiryTime;
private readonly TimeSpan? waitTime;
private readonly TimeSpan? retryTime;
@@ -233,12 +251,12 @@ private async Task StartAsync()
}
}
- var status = GetFailedRedLockStatus(lockSummary);
+ var failedStatus = GetFailedRedLockStatus(lockSummary);
// give up
logger.LogDebug($"Could not acquire quorum after {quorumRetryCount} attempts, giving up: {Resource} ({LockId}). {lockSummary}.");
- return (status, lockSummary);
+ return (failedStatus, lockSummary);
}
private async Task<(RedLockStatus, RedLockInstanceSummary)> AcquireAsync()
@@ -279,12 +297,12 @@ private async Task StartAsync()
}
}
- var status = GetFailedRedLockStatus(lockSummary);
+ var failedStatus = GetFailedRedLockStatus(lockSummary);
// give up
logger.LogDebug($"Could not acquire quorum after {quorumRetryCount} attempts, giving up: {Resource} ({LockId}). {lockSummary}.");
- return (status, lockSummary);
+ return (failedStatus, lockSummary);
}
private void StartAutoExtendTimer()
@@ -293,7 +311,7 @@ private void StartAutoExtendTimer()
logger.LogDebug($"Starting auto extend timer with {interval}ms interval");
- lockKeepaliveTimer = new Timer(
+ lockKeepAliveTimer = new Timer(
state =>
{
try
@@ -565,11 +583,11 @@ protected virtual void Dispose(bool disposing)
{
lock (lockObject)
{
- if (lockKeepaliveTimer != null)
+ if (lockKeepAliveTimer != null)
{
- lockKeepaliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
- lockKeepaliveTimer.Dispose();
- lockKeepaliveTimer = null;
+ lockKeepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ lockKeepAliveTimer.Dispose();
+ lockKeepAliveTimer = null;
}
}
}
@@ -629,14 +647,14 @@ private static RedLockInstanceSummary PopulateRedLockResult(IEnumerable
internal void StopKeepAliveTimer()
{
- if (lockKeepaliveTimer == null)
- {
+ if (lockKeepAliveTimer == null)
+ {
return;
}
logger.LogDebug("Stopping auto extend timer");
- lockKeepaliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ lockKeepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
}