Improved locking code.

- Moved DatabaseLock into Framework and making it the default lock provider.
- Removed unnecessary thread synchronization code.
- Allowing multiple lock acquisitions from the same machine (simulating a "lease").
- Updated unit tests.
This commit is contained in:
Sipke Schoorstra
2015-08-11 16:43:48 +01:00
parent cb6835aba3
commit 77b8df516a
19 changed files with 106 additions and 105 deletions

View File

@@ -182,7 +182,6 @@
<Compile Include="Stubs\MessageChannelSelectorStub.cs" /> <Compile Include="Stubs\MessageChannelSelectorStub.cs" />
<Compile Include="Stubs\ShapeDisplayStub.cs" /> <Compile Include="Stubs\ShapeDisplayStub.cs" />
<Compile Include="Tags\Services\TagsServiceTests.cs" /> <Compile Include="Tags\Services\TagsServiceTests.cs" />
<Compile Include="TaskLease\DatabaseLockTests.cs" />
<Compile Include="Themes\Services\ThemeServiceTests.cs" /> <Compile Include="Themes\Services\ThemeServiceTests.cs" />
<Compile Include="Users\Controllers\AccountControllerTests.cs" /> <Compile Include="Users\Controllers\AccountControllerTests.cs" />
<Compile Include="Users\Services\UserServiceTests.cs" /> <Compile Include="Users\Services\UserServiceTests.cs" />

View File

@@ -292,6 +292,7 @@
<Compile Include="Stubs\StubVirtualPathMonitor.cs" /> <Compile Include="Stubs\StubVirtualPathMonitor.cs" />
<Compile Include="Stubs\StubCacheManager.cs" /> <Compile Include="Stubs\StubCacheManager.cs" />
<Compile Include="Stubs\StubWebSiteFolder.cs" /> <Compile Include="Stubs\StubWebSiteFolder.cs" />
<Compile Include="Tasks\DatabaseLockTests.cs" />
<Compile Include="Tasks\DistributedLockServiceTests.cs" /> <Compile Include="Tasks\DistributedLockServiceTests.cs" />
<Compile Include="Time\TimeZoneSelectorTests.cs" /> <Compile Include="Time\TimeZoneSelectorTests.cs" />
<Compile Include="UI\Resources\ResourceManagerTests.cs" /> <Compile Include="UI\Resources\ResourceManagerTests.cs" />

View File

@@ -1,23 +1,27 @@
using System; using System;
using Orchard.Tasks.Locking; using Orchard.Tasks.Locking.Services;
namespace Orchard.Tests.Stubs { namespace Orchard.Tests.Stubs {
public class StubDistributedLock : IDistributedLock { public class StubDistributedLock : IDistributedLock {
public static bool IsAcquired { get; private set; } public static bool IsAcquired { get; private set; }
public static string AcquiredByMachineName { get; private set; }
public bool IsDisposed { get; private set; } public bool IsDisposed { get; private set; }
public bool TryAcquire(string name, TimeSpan maxLifetime) { public bool TryAcquire(string name, string machineName, TimeSpan maxLifetime) {
if (IsAcquired) if (IsAcquired && machineName != AcquiredByMachineName)
return false; return false;
return IsAcquired = true; IsAcquired = true;
AcquiredByMachineName = machineName;
return true;
} }
public void Dispose() { public void Dispose() {
IsDisposed = true; IsDisposed = true;
IsAcquired = false; IsAcquired = false;
AcquiredByMachineName = null;
} }
} }
} }

View File

@@ -2,8 +2,12 @@
namespace Orchard.Tests.Stubs { namespace Orchard.Tests.Stubs {
public class StubMachineNameProvider : IMachineNameProvider { public class StubMachineNameProvider : IMachineNameProvider {
public StubMachineNameProvider() {
MachineName = "Orchard Machine";
}
public string MachineName { get; set; }
public string GetMachineName() { public string GetMachineName() {
return "Orchard Machine"; return MachineName;
} }
} }
} }

View File

@@ -4,12 +4,15 @@ using System.Linq;
using Autofac; using Autofac;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Data; using Orchard.Data;
using Orchard.TaskLease.Models; using Orchard.Tasks.Locking.Providers;
using Orchard.TaskLease.Services; using Orchard.Tasks.Locking.Records;
namespace Orchard.Tests.Modules.TaskLease { namespace Orchard.Tests.Tasks {
[TestFixture] [TestFixture]
public class DatabaseLockTests : DatabaseEnabledTestsBase { public class DatabaseLockTests : DatabaseEnabledTestsBase {
private const string MachineName1 = "Orchard Testing Machine 1";
private const string MachineName2 = "Orchard Testing Machine 2";
private const string LockName = "Orchard Test Lock";
private IRepository<DatabaseLockRecord> _databaseLockRepository; private IRepository<DatabaseLockRecord> _databaseLockRepository;
private DatabaseLock _lock; private DatabaseLock _lock;
@@ -32,7 +35,7 @@ namespace Orchard.Tests.Modules.TaskLease {
[Test] [Test]
public void AcquiringLockSucceeds() { public void AcquiringLockSucceeds() {
var lockAcquired = _lock.TryAcquire("Test", TimeSpan.FromSeconds(60)); var lockAcquired = _lock.TryAcquire(LockName, MachineName1, TimeSpan.FromSeconds(60));
Assert.That(lockAcquired, Is.True); Assert.That(lockAcquired, Is.True);
Assert.That(_databaseLockRepository.Table.Count(), Is.EqualTo(1)); Assert.That(_databaseLockRepository.Table.Count(), Is.EqualTo(1));
@@ -40,15 +43,15 @@ namespace Orchard.Tests.Modules.TaskLease {
[Test] [Test]
public void DisposingRemovesLockRecord() { public void DisposingRemovesLockRecord() {
_lock.TryAcquire("Test", TimeSpan.FromSeconds(60)); _lock.TryAcquire(LockName, MachineName1, TimeSpan.FromSeconds(60));
_lock.Dispose(); _lock.Dispose();
Assert.That(_databaseLockRepository.Table.Count(), Is.EqualTo(0)); Assert.That(_databaseLockRepository.Table.Count(), Is.EqualTo(0));
} }
[Test] [Test]
public void AcquiringLockTwiceFails() { public void AcquiringLockTwiceFails() {
var attempt1 = _lock.TryAcquire("Test", TimeSpan.FromSeconds(60)); var attempt1 = _lock.TryAcquire(LockName, MachineName1, TimeSpan.FromSeconds(60));
var attempt2 = _lock.TryAcquire("Test", TimeSpan.FromSeconds(60)); var attempt2 = _lock.TryAcquire(LockName, MachineName2, TimeSpan.FromSeconds(60));
Assert.That(attempt1, Is.True); Assert.That(attempt1, Is.True);
Assert.That(attempt2, Is.False); Assert.That(attempt2, Is.False);
@@ -56,8 +59,8 @@ namespace Orchard.Tests.Modules.TaskLease {
[Test] [Test]
public void AcquiringExpiredLockSucceeds() { public void AcquiringExpiredLockSucceeds() {
var attempt1 = _lock.TryAcquire("Test", TimeSpan.FromSeconds(60)); var attempt1 = _lock.TryAcquire(LockName, MachineName1, TimeSpan.FromSeconds(60));
var attempt2 = _lock.TryAcquire("Test", TimeSpan.FromSeconds(-1)); // Treat the previosuly stored lock as immediately expired. var attempt2 = _lock.TryAcquire(LockName, MachineName2, TimeSpan.FromSeconds(-1)); // Treat the previosuly stored lock as immediately expired.
Assert.That(attempt1, Is.True); Assert.That(attempt1, Is.True);
Assert.That(attempt2, Is.True); Assert.That(attempt2, Is.True);

View File

@@ -5,17 +5,19 @@ using System.Threading.Tasks;
using Autofac; using Autofac;
using NUnit.Framework; using NUnit.Framework;
using Orchard.Environment; using Orchard.Environment;
using Orchard.Tasks.Locking; using Orchard.Tasks.Locking.Services;
using Orchard.Tests.Stubs; using Orchard.Tests.Stubs;
namespace Orchard.Tests.Tasks { namespace Orchard.Tests.Tasks {
[TestFixture] [TestFixture]
public class DistributedLockServiceTests : ContainerTestBase { public class DistributedLockServiceTests : ContainerTestBase {
private const string LockName = "Orchard Test Lock";
private DistributedLockService _distributedLockService; private DistributedLockService _distributedLockService;
private StubMachineNameProvider _stubMachineNameProvider;
protected override void Register(ContainerBuilder builder) { protected override void Register(ContainerBuilder builder) {
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>(); builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<StubMachineNameProvider>().As<IMachineNameProvider>(); builder.RegisterType<StubMachineNameProvider>().As<IMachineNameProvider>().SingleInstance();
builder.RegisterType<StubDistributedLock>().As<IDistributedLock>(); builder.RegisterType<StubDistributedLock>().As<IDistributedLock>();
builder.RegisterType<DistributedLockService>().AsSelf(); builder.RegisterType<DistributedLockService>().AsSelf();
builder.RegisterInstance(new Work<IDistributedLock>(resolve => _container.Resolve<IDistributedLock>())).AsSelf(); builder.RegisterInstance(new Work<IDistributedLock>(resolve => _container.Resolve<IDistributedLock>())).AsSelf();
@@ -23,22 +25,35 @@ namespace Orchard.Tests.Tasks {
protected override void Resolve(ILifetimeScope container) { protected override void Resolve(ILifetimeScope container) {
_distributedLockService = container.Resolve<DistributedLockService>(); _distributedLockService = container.Resolve<DistributedLockService>();
_stubMachineNameProvider = (StubMachineNameProvider)container.Resolve<IMachineNameProvider>();
} }
[Test] [Test]
public void AcquiringLockSucceeds() { public void AcquiringLockSucceeds() {
IDistributedLock @lock; IDistributedLock @lock;
var lockAcquired = _distributedLockService.TryAcquireLock("Test", TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock); var lockAcquired = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
Assert.That(lockAcquired, Is.True); Assert.That(lockAcquired, Is.True);
} }
[Test]
public void AcquiringLockTwiceOnSameMachineSucceeds() {
IDistributedLock @lock;
var attempt1 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
var attempt2 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
Assert.That(attempt1, Is.True);
Assert.That(attempt2, Is.True);
}
[Test] [Test]
public void AcquiringLockTwiceFails() { public void AcquiringLockTwiceFails() {
IDistributedLock @lock; IDistributedLock @lock;
var attempt1 = _distributedLockService.TryAcquireLock("Test", TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock); _stubMachineNameProvider.MachineName = "Orchard Test Machine 1";
var attempt2 = _distributedLockService.TryAcquireLock("Test", TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock); var attempt1 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
_stubMachineNameProvider.MachineName = "Orchard Test Machine 2";
var attempt2 = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
Assert.That(attempt1, Is.True); Assert.That(attempt1, Is.True);
Assert.That(attempt2, Is.False); Assert.That(attempt2, Is.False);
@@ -52,7 +67,8 @@ namespace Orchard.Tests.Tasks {
foreach (var index in Enumerable.Range(0, 10).AsParallel()) { foreach (var index in Enumerable.Range(0, 10).AsParallel()) {
var task = Task.Factory.StartNew(() => { var task = Task.Factory.StartNew(() => {
IDistributedLock @lock; IDistributedLock @lock;
var attempt = _distributedLockService.TryAcquireLock("Test", TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock); _stubMachineNameProvider.MachineName = "Orchard Test Machine " + (index + 1);
var attempt = _distributedLockService.TryAcquireLock(LockName, TimeSpan.FromSeconds(60), TimeSpan.Zero, out @lock);
attempts.Add(attempt); attempts.Add(attempt);
}); });

View File

@@ -17,14 +17,5 @@ namespace Orchard.TaskLease {
return 1; return 1;
} }
public int UpdateFrom1() {
SchemaBuilder.CreateTable("DatabaseLockRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Name", column => column.NotNull().WithLength(256))
.Column<DateTime>("AcquiredUtc"));
return 2;
}
} }
} }

View File

@@ -4,5 +4,6 @@ Author: The Orchard Team
Website: http://orchardtasklease.codeplex.com Website: http://orchardtasklease.codeplex.com
Version: 1.9.1 Version: 1.9.1
OrchardVersion: 1.9 OrchardVersion: 1.9
Description: Provides services to synchronize tasks in a farm environment LifecycleStatus: Deprecated
Description: Provides services to synchronize tasks in a farm environment.
Category: Hosting Category: Hosting

View File

@@ -76,7 +76,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Web.config" /> <Content Include="Web.config" />
<Compile Include="Models\DatabaseLockRecord.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" /> <Content Include="Module.txt" />
</ItemGroup> </ItemGroup>
@@ -86,7 +85,6 @@
</Compile> </Compile>
<Compile Include="Models\TaskLeaseRecord.cs" /> <Compile Include="Models\TaskLeaseRecord.cs" />
<Compile Include="Services\ITaskLeaseService.cs" /> <Compile Include="Services\ITaskLeaseService.cs" />
<Compile Include="Services\DatabaseLock.cs" />
<Compile Include="Services\TaskLeaseService.cs" /> <Compile Include="Services\TaskLeaseService.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />

View File

@@ -1133,7 +1133,7 @@ Global
{642A49D7-8752-4177-80D6-BFBBCFAD3DE0} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {642A49D7-8752-4177-80D6-BFBBCFAD3DE0} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{966EC390-3C7F-4D98-92A6-F0F30D02E9B1} = {902528F6-1444-42A3-8B75-A54B775B539C} {966EC390-3C7F-4D98-92A6-F0F30D02E9B1} = {902528F6-1444-42A3-8B75-A54B775B539C}
{3158C928-888C-4A84-8BC1-4A8257489538} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {3158C928-888C-4A84-8BC1-4A8257489538} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{3F72A4E9-7B72-4260-B010-C16EC54F9BAF} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {3F72A4E9-7B72-4260-B010-C16EC54F9BAF} = {902528F6-1444-42A3-8B75-A54B775B539C}
{E07AFA7E-7B36-44C3-A537-AFCCAA93EA7A} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {E07AFA7E-7B36-44C3-A537-AFCCAA93EA7A} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
{5531E894-D259-45A3-AA61-26DBE720C1CE} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {5531E894-D259-45A3-AA61-26DBE720C1CE} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{2969635F-D9C3-4D01-890D-437B46659690} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {2969635F-D9C3-4D01-890D-437B46659690} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}

View File

@@ -3,7 +3,7 @@ using System.Linq;
using Orchard.Environment; using Orchard.Environment;
using Orchard.Environment.Features; using Orchard.Environment.Features;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Tasks.Locking; using Orchard.Tasks.Locking.Services;
namespace Orchard.Data.Migration { namespace Orchard.Data.Migration {
/// <summary> /// <summary>

View File

@@ -397,10 +397,12 @@
<Compile Include="Services\IJsonConverter.cs" /> <Compile Include="Services\IJsonConverter.cs" />
<Compile Include="Settings\CurrentSiteWorkContext.cs" /> <Compile Include="Settings\CurrentSiteWorkContext.cs" />
<Compile Include="Settings\ResourceDebugMode.cs" /> <Compile Include="Settings\ResourceDebugMode.cs" />
<Compile Include="Tasks\Locking\DefaultLock.cs" /> <Compile Include="Tasks\Locking\Migrations\FrameworkMigrations.cs" />
<Compile Include="Tasks\Locking\IDistributedLockService.cs" /> <Compile Include="Tasks\Locking\Providers\DatabaseLock.cs" />
<Compile Include="Tasks\Locking\IDistributedLock.cs" /> <Compile Include="Tasks\Locking\Services\IDistributedLockService.cs" />
<Compile Include="Tasks\Locking\DistributedLockService.cs" /> <Compile Include="Tasks\Locking\Services\IDistributedLock.cs" />
<Compile Include="Tasks\Locking\Services\DistributedLockService.cs" />
<Compile Include="Tasks\Locking\Records\DatabaseLockRecord.cs" />
<Compile Include="Themes\CurrentThemeWorkContext.cs" /> <Compile Include="Themes\CurrentThemeWorkContext.cs" />
<Compile Include="Themes\ThemeManager.cs" /> <Compile Include="Themes\ThemeManager.cs" />
<Compile Include="Time\CurrentTimeZoneWorkContext.cs" /> <Compile Include="Time\CurrentTimeZoneWorkContext.cs" />

View File

@@ -1,31 +0,0 @@
using System;
namespace Orchard.Tasks.Locking {
public class DefaultLock : IDistributedLock {
private readonly IStaticLockSemaphore _semaphore;
public DefaultLock(IStaticLockSemaphore semaphore) {
_semaphore = semaphore;
}
public bool TryAcquire(string name, TimeSpan maxLifetime) {
if (_semaphore.IsAcquired)
return false;
return _semaphore.IsAcquired = true;
}
public void Dispose() {
_semaphore.IsAcquired = false;
}
public interface IStaticLockSemaphore : ISingletonDependency {
bool IsAcquired { get; set; }
}
public class StaticLockSemaphore : IStaticLockSemaphore {
public bool IsAcquired { get; set; }
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using Orchard.Data.Migration;
namespace Orchard.Tasks.Locking.Migrations {
public class FrameworkMigrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("DatabaseLockRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Name", column => column.NotNull().WithLength(256))
.Column<string>("MachineName", column => column.WithLength(256))
.Column<DateTime>("AcquiredUtc"));
return 1;
}
}
}

View File

@@ -3,20 +3,18 @@ using System.Data;
using System.Linq; using System.Linq;
using Autofac; using Autofac;
using Orchard.Data; using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.Exceptions; using Orchard.Exceptions;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Services; using Orchard.Services;
using Orchard.TaskLease.Models; using Orchard.Tasks.Locking.Records;
using Orchard.Tasks.Locking; using Orchard.Tasks.Locking.Services;
using Orchard.Validation; using Orchard.Validation;
namespace Orchard.TaskLease.Services { namespace Orchard.Tasks.Locking.Providers {
/// <summary> /// <summary>
/// Provides a database driven implementation of <see cref="IDistributedLock" /> /// Provides a database driven implementation of <see cref="IDistributedLock" />
/// </summary> /// </summary>
[OrchardSuppressDependency("Orchard.Tasks.Locking.DefaultLock")]
public class DatabaseLock : IDistributedLock { public class DatabaseLock : IDistributedLock {
private readonly ILifetimeScope _lifetimeScope; private readonly ILifetimeScope _lifetimeScope;
private readonly IClock _clock; private readonly IClock _clock;
@@ -34,7 +32,7 @@ namespace Orchard.TaskLease.Services {
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
public bool TryAcquire(string name, TimeSpan maxLifetime) { public bool TryAcquire(string name, string machineName, TimeSpan maxLifetime) {
if(_isDisposed) if(_isDisposed)
throw new ObjectDisposedException("DatabaseLock"); throw new ObjectDisposedException("DatabaseLock");
@@ -53,9 +51,10 @@ namespace Orchard.TaskLease.Services {
var record = repository.Table.FirstOrDefault(x => x.Name == name); var record = repository.Table.FirstOrDefault(x => x.Name == name);
if (record != null) { if (record != null) {
// There is a nexisting lock, but check if it has expired. // There is an existing lock, but check if it has expired or if the current machine is requesting the lock.
var isExpired = record.AcquiredUtc + maxLifetime < _clock.UtcNow; var isExpired = record.AcquiredUtc + maxLifetime < _clock.UtcNow;
if (isExpired) { var isCurrentMachine = record.MachineName == machineName;
if (isExpired || isCurrentMachine) {
repository.Delete(record); repository.Delete(record);
record = null; record = null;
} }
@@ -66,6 +65,7 @@ namespace Orchard.TaskLease.Services {
if (canAcquire) { if (canAcquire) {
record = new DatabaseLockRecord { record = new DatabaseLockRecord {
Name = name, Name = name,
MachineName = machineName,
AcquiredUtc = _clock.UtcNow AcquiredUtc = _clock.UtcNow
}; };
repository.Create(record); repository.Create(record);

View File

@@ -1,9 +1,10 @@
using System; using System;
namespace Orchard.TaskLease.Models { namespace Orchard.Tasks.Locking.Records {
public class DatabaseLockRecord { public class DatabaseLockRecord {
public virtual int Id { get; set; } public virtual int Id { get; set; }
public virtual string Name { get; set; } public virtual string Name { get; set; }
public virtual string MachineName { get; set; }
public virtual DateTime? AcquiredUtc { get; set; } public virtual DateTime? AcquiredUtc { get; set; }
} }
} }

View File

@@ -4,9 +4,8 @@ using System.Threading.Tasks;
using Orchard.Environment; using Orchard.Environment;
using Orchard.Logging; using Orchard.Logging;
namespace Orchard.Tasks.Locking { namespace Orchard.Tasks.Locking.Services {
public class DistributedLockService : IDistributedLockService { public class DistributedLockService : IDistributedLockService {
private readonly ReaderWriterLockSlim _rwl = new ReaderWriterLockSlim();
private readonly IWorkContextAccessor _wca; private readonly IWorkContextAccessor _wca;
private readonly IMachineNameProvider _machineNameProvider; private readonly IMachineNameProvider _machineNameProvider;
@@ -22,31 +21,26 @@ namespace Orchard.Tasks.Locking {
var machineName = _machineNameProvider.GetMachineName(); var machineName = _machineNameProvider.GetMachineName();
@lock = Resolve<IDistributedLock>(); @lock = Resolve<IDistributedLock>();
if (_rwl.TryEnterWriteLock(0)) { try {
try { var waitedTime = TimeSpan.Zero;
var waitedTime = TimeSpan.Zero; var waitTime = TimeSpan.FromMilliseconds(timeout.TotalMilliseconds / 10);
var waitTime = TimeSpan.FromMilliseconds(timeout.TotalMilliseconds / 10); bool acquired;
bool acquired;
while (!(acquired = @lock.TryAcquire(name, maxLifetime)) && waitedTime < timeout) { while (!(acquired = @lock.TryAcquire(name, machineName, maxLifetime)) && waitedTime < timeout) {
Task.Delay(timeout).ContinueWith(t => { Task.Delay(timeout).ContinueWith(t => {
waitedTime += waitTime; waitedTime += waitTime;
}).Wait(); }).Wait();
} }
if (acquired) { if (acquired) {
Logger.Debug("Successfully acquired a lock named {0} on machine {1}.", name, machineName); Logger.Debug("Successfully acquired a lock named {0} on machine {1}.", name, machineName);
return true; return true;
}
}
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire a lock named {0} on machine {1}.", name, machineName);
throw;
}
finally {
_rwl.ExitWriteLock();
} }
} }
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire a lock named {0} on machine {1}.", name, machineName);
throw;
}
Logger.Debug("Could not acquire a lock named {0} on machine {1}.", name, machineName); Logger.Debug("Could not acquire a lock named {0} on machine {1}.", name, machineName);
return false; return false;

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Orchard.Tasks.Locking { namespace Orchard.Tasks.Locking.Services {
/// <summary> /// <summary>
/// Provides a lock on a provided name. /// Provides a lock on a provided name.
/// </summary> /// </summary>
@@ -9,8 +9,9 @@ namespace Orchard.Tasks.Locking {
/// Tries to acquire a lock on the specified name. /// Tries to acquire a lock on the specified name.
/// </summary> /// </summary>
/// <param name="name">The name to use for the lock.</param> /// <param name="name">The name to use for the lock.</param>
/// <param name="machineName">The machine name trying to acquire a lock.</param>
/// <param name="maxLifetime">The maximum amount of time the lock is allowed. This is a safety net in case the caller fails to release the lock.</param> /// <param name="maxLifetime">The maximum amount of time the lock is allowed. This is a safety net in case the caller fails to release the lock.</param>
/// <returns>Returns true if a lock was acquired, false otherwise.</returns> /// <returns>Returns true if a lock was acquired, false otherwise.</returns>
bool TryAcquire(string name, TimeSpan maxLifetime); bool TryAcquire(string name, string machineName, TimeSpan maxLifetime);
} }
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Orchard.Tasks.Locking { namespace Orchard.Tasks.Locking.Services {
/// <summary> /// <summary>
/// Provides distributed locking functionality. /// Provides distributed locking functionality.
/// </summary> /// </summary>