ServiceStack.Redis.Support.Locking.DistributedLock.Lock C# (CSharp) Method

Lock() public method

acquire distributed, non-reentrant lock on key
public Lock ( string key, int acquisitionTimeout, int lockTimeout, long &lockExpire, IRedisClient client ) : long
key string global key for this lock
acquisitionTimeout int timeout for acquiring lock
lockTimeout int timeout for lock, in seconds (stored as value against lock key)
lockExpire long
client IRedisClient
return long
        public virtual long Lock(string key, int acquisitionTimeout, int lockTimeout, out long lockExpire, IRedisClient client)
		{
		    lockExpire = 0;

            // cannot lock on a null key
            if (key == null)
                return LOCK_NOT_ACQUIRED;

			const int sleepIfLockSet = 200;
			acquisitionTimeout *= 1000; //convert to ms
			int tryCount = (acquisitionTimeout / sleepIfLockSet) + 1;

			var ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
			var newLockExpire = CalculateLockExpire(ts, lockTimeout);

            var localClient = (RedisClient)client;
            long wasSet = localClient.SetNX(key, BitConverter.GetBytes(newLockExpire));
			int totalTime = 0;
            while (wasSet == LOCK_NOT_ACQUIRED && totalTime < acquisitionTimeout)
			{
				int count = 0;
				while (wasSet == 0 && count < tryCount && totalTime < acquisitionTimeout)
				{
					TaskUtils.Sleep(sleepIfLockSet);
					totalTime += sleepIfLockSet;					
					ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
					newLockExpire = CalculateLockExpire(ts, lockTimeout);
                    wasSet = localClient.SetNX(key, BitConverter.GetBytes(newLockExpire));
					count++;
				}
				// acquired lock!
                if (wasSet != LOCK_NOT_ACQUIRED) break;

				// handle possibliity of crashed client still holding the lock
                using (var pipe = localClient.CreatePipeline())
				{
				    long lockValue=0;
					pipe.QueueCommand(r => ((RedisNativeClient)r).Watch(key));
					pipe.QueueCommand(r => ((RedisNativeClient)r).Get(key), x => lockValue = (x != null) ? BitConverter.ToInt64(x,0) : 0);
					pipe.Flush();

					// if lock value is 0 (key is empty), or expired, then we can try to acquire it
                    ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
					if (lockValue < ts.TotalSeconds)
					{
						ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
						newLockExpire = CalculateLockExpire(ts, lockTimeout);
						using (var trans = localClient.CreateTransaction())
						{
							var expire = newLockExpire;
							trans.QueueCommand(r => ((RedisNativeClient)r).Set(key, BitConverter.GetBytes(expire)));
							if (trans.Commit())
								wasSet = LOCK_RECOVERED; //recovered lock!
						}
					}
					else
					{
                        localClient.UnWatch();
					}
				}
                if (wasSet != LOCK_NOT_ACQUIRED) break;
				TaskUtils.Sleep(sleepIfLockSet);
				totalTime += sleepIfLockSet;
			}
            if (wasSet != LOCK_NOT_ACQUIRED)
            {
                lockExpire = newLockExpire;
            }
		    return wasSet;

		}

Usage Example

        public void Can_create_distributed_lock()
        {
            var key = "lockkey";
            int lockTimeout = 2;

            var distributedLock = new DistributedLock();
            long lockExpire;
            Assert.AreEqual(distributedLock.Lock(key, lockTimeout, lockTimeout, out lockExpire, Redis), DistributedLock.LOCK_ACQUIRED);

            //can't re-lock
            distributedLock = new DistributedLock();
            Assert.AreEqual(distributedLock.Lock(key, lockTimeout, lockTimeout, out lockExpire, Redis), DistributedLock.LOCK_NOT_ACQUIRED);

            // re-acquire lock after timeout
            Thread.Sleep(lockTimeout * 1000 + 1000);
            distributedLock = new DistributedLock();
            Assert.AreEqual(distributedLock.Lock(key, lockTimeout, lockTimeout, out lockExpire, Redis), DistributedLock.LOCK_RECOVERED);


            Assert.IsTrue(distributedLock.Unlock(key, lockExpire, Redis));

            //can now lock
            distributedLock = new DistributedLock();
            Assert.AreEqual(distributedLock.Lock(key, lockTimeout, lockTimeout, out lockExpire, Redis), DistributedLock.LOCK_ACQUIRED);


            //cleanup
            Assert.IsTrue(distributedLock.Unlock(key, lockExpire, Redis));
        }
All Usage Examples Of ServiceStack.Redis.Support.Locking.DistributedLock::Lock