Follow-up to: Waiting for a lock to release with Thread.Sleep()? Waiting for a lock to release with Thread.Sleep()?
Update: Since I still have the comment from Brian Reichle Brian Reichle in my ears, I've moved on to make the waiting and acquiring of the lock one atomic operation.
Follow-up to: Waiting for a lock to release with Thread.Sleep()?
Update: Since I still have the comment from Brian Reichle in my ears, I've moved on to make the waiting and acquiring of the lock one atomic operation.
Follow-up to: Waiting for a lock to release with Thread.Sleep()?
Update: Since I still have the comment from Brian Reichle in my ears, I've moved on to make the waiting and acquiring of the lock one atomic operation.
Since II've found the time I tried to rewrite my WaitForLock
-Method to utilize the Quartz.NET Scheduler which I've been using for some months now for other stuff. It got a little bit more complicated but at least it now misses the dreaded Thread.Sleep()
completely, which is a big improvement for me.
/// <summary>Waits for the lock to be released within the given timeout.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="timeout">The timeout in seconds.</param>
/// <returns>True if the Lock was released.</returns>
public bool WaitForLock(String lockName, Int32 timeout)
{
// IsLocked(String) does query the database for the status
if(!locker.IsLocked(lockName))
return true;
using(ManualResetEvent reset = new ManualResetEvent(False)
{
ScheduleLockWaiter(lockName, timeout, reset);
reset.WaitOne(timeout * 1000);
// WARNING: This overload is only available in: 4, 3.5 SP1, 3.0 SP2, 2.0 SP2
// I spend a half day trying to figure that out.
}
return !locker.IsLocked(lockName);
}
/// <summary>Create and schedule the job to wait for the lock.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="repeat">The times it shall repeat.</param>
/// <param name="reset">The ManualResetEvent to report on.</param>
private void ScheduleLockWaiter(String lockName, Int32 repeat, ManualResetEvent reset)
{
// Utilizing Quartz.NET
String name = "LockJob_" + lockName;
Trigger trigger = TriggerUtils.MakeSecondlyTrigger("LockTrigger_" + lockName, 1, repeat - 1);
JobDetail job = new JobDetail(name, _lockJobGroup, typeof(LockJob));
job.JobDataMap.Add("Locker", _locker);
job.JobDataMap.Add("Reset", reset);
job.JobDataMap.Add("LockName", lockName);
if(_scheduler.GetJobDetail(name, _lockJobGroup) != null)
_scheduler.UnscheduleJob(name, _lockJobGroup);
_scheduler.ScheduleJob(job, trigger);
}
// Further down the road, our Job-Class
public class LockJob : IJob
{
public void Execute(JobExecutionContext context)
{
ILocker locker = (ILocker)context.JobDetail.JobDataMap.Get("Locker");
ManualResetEvent reset = (ManualResetEvent)context.JobDetail.JobDataMap.Get("reset");
String lockName = (String)context.JobDetail.JobDataMap.Get("LockName");
if(!locker.IsLocked(lockName))
{
context.Scheduler.UndscheduleJob(context.Trigger.Name, context.Trigger.Group);
reset.Set();
}
}
}
Update: Since I still have the comment from Brian Reichle in my ears, I've moved on to make the waiting and acquiring of the lock one atomic operation.
Also the scheduled job has changed to directly acquire the Lock. Yes, I know that it tries to lock it twice on success (I just realized that, but did not see a way to change that).
/// <summary>Tries to acquire the Lock within the given timeout.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="timeout">The timeout in seconds.</param>
/// <returns>True if the Lock could be acquired.</returns>
public bool WaitForLock(String lockName, Int32 timeout)
{
// boolean ILocker.Lock(String lockName)
// Returns true if it was able to engage the lock.
if(locker.Lock(lockName))
return true; // Easy way out
using(ManualResetEvent reset = new ManualResetEvent(False)
{
ScheduleLockWaiter(lockName, timeout, reset);
reset.WaitOne(timeout * 1000, false);
}
return locker.Lock(lockName);
}
// Further down the road, our Job-Class
public class LockJob : IJob
{
public void Execute(JobExecutionContext context)
{
ILocker locker = (ILocker)context.JobDetail.JobDataMap.Get("Locker");
ManualResetEvent reset = (ManualResetEvent)context.JobDetail.JobDataMap.Get("reset");
String lockName = (String)context.JobDetail.JobDataMap.Get("LockName");
if(locker.Lock(lockName))
{
context.Scheduler.UndscheduleJob(context.Trigger.Name, context.Trigger.Group);
reset.Set();
}
}
}
Since I found the time I tried to rewrite my WaitForLock
-Method to utilize Quartz.NET Scheduler which I've been using for some months now. It got a little bit more complicated but at least it now misses the dreaded Thread.Sleep()
completely, which is a big improvement for me.
/// <summary>Waits for the lock to be released within the given timeout.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="timeout">The timeout in seconds.</param>
/// <returns>True if the Lock was released.</returns>
public bool WaitForLock(String lockName, Int32 timeout)
{
// IsLocked(String) does query the database for the status
if(!locker.IsLocked(lockName))
return true;
using(ManualResetEvent reset = new ManualResetEvent(False)
{
ScheduleLockWaiter(lockName, timeout, reset);
reset.WaitOne(timeout * 1000);
}
return !locker.IsLocked(lockName);
}
/// <summary>Create and schedule the job to wait for the lock.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="repeat">The times it shall repeat.</param>
/// <param name="reset">The ManualResetEvent to report on.</param>
private void ScheduleLockWaiter(String lockName, Int32 repeat, ManualResetEvent reset)
{
// Utilizing Quartz.NET
String name = "LockJob_" + lockName;
Trigger trigger = TriggerUtils.MakeSecondlyTrigger("LockTrigger_" + lockName, 1, repeat - 1);
JobDetail job = new JobDetail(name, _lockJobGroup, typeof(LockJob));
job.JobDataMap.Add("Locker", _locker);
job.JobDataMap.Add("Reset", reset);
job.JobDataMap.Add("LockName", lockName);
if(_scheduler.GetJobDetail(name, _lockJobGroup) != null)
_scheduler.UnscheduleJob(name, _lockJobGroup);
_scheduler.ScheduleJob(job, trigger);
}
// Further down the road, our Job-Class
public class LockJob : IJob
{
public void Execute(JobExecutionContext context)
{
ILocker locker = (ILocker)context.JobDetail.JobDataMap.Get("Locker");
ManualResetEvent reset = (ManualResetEvent)context.JobDetail.JobDataMap.Get("reset");
String lockName = (String)context.JobDetail.JobDataMap.Get("LockName");
if(!locker.IsLocked(lockName))
{
context.Scheduler.UndscheduleJob(context.Trigger.Name, context.Trigger.Group);
reset.Set();
}
}
}
I've found the time I tried to rewrite my WaitForLock
-Method to utilize the Quartz.NET Scheduler which I've been using for some months now for other stuff. It got a little bit more complicated but at least it now misses the dreaded Thread.Sleep()
completely, which is a big improvement for me.
/// <summary>Waits for the lock to be released within the given timeout.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="timeout">The timeout in seconds.</param>
/// <returns>True if the Lock was released.</returns>
public bool WaitForLock(String lockName, Int32 timeout)
{
// IsLocked(String) does query the database for the status
if(!locker.IsLocked(lockName))
return true;
using(ManualResetEvent reset = new ManualResetEvent(False)
{
ScheduleLockWaiter(lockName, timeout, reset);
reset.WaitOne(timeout * 1000);
// WARNING: This overload is only available in: 4, 3.5 SP1, 3.0 SP2, 2.0 SP2
// I spend a half day trying to figure that out.
}
return !locker.IsLocked(lockName);
}
/// <summary>Create and schedule the job to wait for the lock.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="repeat">The times it shall repeat.</param>
/// <param name="reset">The ManualResetEvent to report on.</param>
private void ScheduleLockWaiter(String lockName, Int32 repeat, ManualResetEvent reset)
{
// Utilizing Quartz.NET
String name = "LockJob_" + lockName;
Trigger trigger = TriggerUtils.MakeSecondlyTrigger("LockTrigger_" + lockName, 1, repeat - 1);
JobDetail job = new JobDetail(name, _lockJobGroup, typeof(LockJob));
job.JobDataMap.Add("Locker", _locker);
job.JobDataMap.Add("Reset", reset);
job.JobDataMap.Add("LockName", lockName);
if(_scheduler.GetJobDetail(name, _lockJobGroup) != null)
_scheduler.UnscheduleJob(name, _lockJobGroup);
_scheduler.ScheduleJob(job, trigger);
}
// Further down the road, our Job-Class
public class LockJob : IJob
{
public void Execute(JobExecutionContext context)
{
ILocker locker = (ILocker)context.JobDetail.JobDataMap.Get("Locker");
ManualResetEvent reset = (ManualResetEvent)context.JobDetail.JobDataMap.Get("reset");
String lockName = (String)context.JobDetail.JobDataMap.Get("LockName");
if(!locker.IsLocked(lockName))
{
context.Scheduler.UndscheduleJob(context.Trigger.Name, context.Trigger.Group);
reset.Set();
}
}
}
Update: Since I still have the comment from Brian Reichle in my ears, I've moved on to make the waiting and acquiring of the lock one atomic operation.
Also the scheduled job has changed to directly acquire the Lock. Yes, I know that it tries to lock it twice on success (I just realized that, but did not see a way to change that).
/// <summary>Tries to acquire the Lock within the given timeout.</summary>
/// <param name="lockName">The name of the lock.</param>
/// <param name="timeout">The timeout in seconds.</param>
/// <returns>True if the Lock could be acquired.</returns>
public bool WaitForLock(String lockName, Int32 timeout)
{
// boolean ILocker.Lock(String lockName)
// Returns true if it was able to engage the lock.
if(locker.Lock(lockName))
return true; // Easy way out
using(ManualResetEvent reset = new ManualResetEvent(False)
{
ScheduleLockWaiter(lockName, timeout, reset);
reset.WaitOne(timeout * 1000, false);
}
return locker.Lock(lockName);
}
// Further down the road, our Job-Class
public class LockJob : IJob
{
public void Execute(JobExecutionContext context)
{
ILocker locker = (ILocker)context.JobDetail.JobDataMap.Get("Locker");
ManualResetEvent reset = (ManualResetEvent)context.JobDetail.JobDataMap.Get("reset");
String lockName = (String)context.JobDetail.JobDataMap.Get("LockName");
if(locker.Lock(lockName))
{
context.Scheduler.UndscheduleJob(context.Trigger.Name, context.Trigger.Group);
reset.Set();
}
}
}
Waiting for a lock to release with ManualResetEvent and Quartz.
I'm not quietquite sure if I have improved something, or created a beast which will devour me someday. Your thoughts on this?
Waiting for a lock to release with ManualResetEvent and Quartz.
I'm not quiet sure if I have improved something, or created a beast which will devour me someday. Your thoughts on this?
Waiting for a lock to release with ManualResetEvent and Quartz
I'm not quite sure if I have improved something, or created a beast which will devour me someday. Your thoughts on this?