예제 #1
0
        /// <summary>
        /// Releases all the locks in the reverse order of acquisition.
        /// </summary>
        public void Release()
        {
            if (this.nodeHaveBeenLocked.Count == 0)
            {
                // Allow the lock to be released again... Will enforce the state transition later.
                return;
            }
            else if (NoReleasingAfterLockRelease && this.lockStage == LockStage.Released)
            {
                throw new InvalidOperationException($"Cannot call {nameof(this.Release)} in stage Released");
            }
            else if (this.lockStage == LockStage.Locked)
            {
                // Immediately mark it as released
                this.lockStage = LockStage.Released;

                while (this.nodeHaveBeenLocked.Count > 0)
                {
                    var lockPair = this.nodeHaveBeenLocked.Pop();
                    if (lockPair.Value == 0)
                    {
                        lockPair.Key.ReleaseReaderLock();
                    }
                    else
                    {
                        lockPair.Key.ReleaseWriterLock();
                    }
                }

                RingMasterEventSource.Log.LockCollectionReleased(Thread.CurrentThread.ManagedThreadId);
            }

            // Do nothing if locks are collected but not acquired.
        }
예제 #2
0
        /// <summary>
        /// Acquires all the locks in a top-down, left-right order.
        /// </summary>
        /// <param name="cancelled">Cancellation token to cancel the long-running lock acquisition</param>
        public void Acquire(ref bool cancelled)
        {
            if (this.lockStage != LockStage.AddingLock)
            {
                throw new InvalidOperationException($"Cannot call {nameof(this.Acquire)} in stage {this.lockStage}");
            }

            if (this.hasWriterLock)
            {
                this.RemoveRedundantLocks();
            }

            // Mark it before potential cancellation
            this.lockStage = LockStage.Locked;

            // For O(1) lock collision check. Collision can only happen within a level, not between different levels.
            // Index refers to the index of the lock object in the following locks list.
            var lockSet = new Dictionary <ILockObject, int>();

            // Ordered collection of each lock to be acquired
            var locks = new List <KeyValuePair <ILockObject, byte> >();

            // For logging and debugging
            var sb = new StringBuilder();

            for (int level = 0; level < this.nodeToBeLocked.Length && !cancelled; level++)
            {
                locks.Clear();

                var idx = 0; // lock index for debugging
                foreach (var nodeLock in this.nodeToBeLocked[level])
                {
                    var lockRequired = nodeLock.Value;
                    var lockObj      = nodeLock.Key.GetLockObject(level, lockRequired == WriterLockValue);

                    // In read free case, no lock is required.
                    if (lockObj == null)
                    {
                        continue;
                    }

                    // Lock collision found. No operation if current one is read because previous one is at least read.
                    int existingLockObjIndex;
                    if (lockSet.TryGetValue(lockObj, out existingLockObjIndex))
                    {
                        // Ignore the collision if the reader is acquired.
                        if (lockRequired == WriterLockValue &&
                            locks[existingLockObjIndex].Value == ReaderLockValue)
                        {
                            locks[existingLockObjIndex] = new KeyValuePair <ILockObject, byte>(lockObj, WriterLockValue);

                            sb.AppendLine(string.Join(",", level.ToString(), nodeLock.Key.Name, nodeLock.Key.BuildPath(), lockRequired.ToString(), "C"));
                        }
                    }
                    else
                    {
                        // No collision
                        lockSet.Add(lockObj, idx);
                        idx++;

                        locks.Add(new KeyValuePair <ILockObject, byte>(lockObj, lockRequired));

                        sb.AppendLine(string.Join(",", level.ToString(), nodeLock.Key.Name, nodeLock.Key.BuildPath(), lockRequired.ToString()));
                    }
                }

                idx = 0;
                foreach (var lockOp in locks)
                {
                    if (cancelled)
                    {
                        break;
                    }

                    var startTime = this.clock.Elapsed;
                    var succeeded = lockOp.Value == ReaderLockValue
                        ? lockOp.Key.AcquireReaderLock(MaxAcquireLockTime)
                        : lockOp.Key.AcquireWriterLock(MaxAcquireLockTime);

                    RingMasterServerInstrumentation.Instance.OnAcquireLock(true, succeeded, level, this.clock.Elapsed - startTime);

                    if (!succeeded)
                    {
                        throw new RetriableOperationException(
                                  $"Lock acquisition timed out after {MaxAcquireLockTime.TotalMilliseconds}: level {level} index {idx} in {sb}");
                    }

                    this.nodeHaveBeenLocked.Push(lockOp);
                    idx++;
                }
            }

            RingMasterEventSource.Log.LockCollectionAcquired(Thread.CurrentThread.ManagedThreadId, sb.ToString());
        }