Exemplo n.º 1
0
        /// <summary>
        /// Finds the leaf quadrant which contains <paramref name="location"/> by
        /// traversing downward starting at <paramref name="initialQuadrant"/> using
        /// upgradeable locks.
        /// </summary>
        /// <returns>The leaf quadrant containing the location. The current thread
        /// will hold an upgradable lock on this quadrant.</returns>
        /// <param name="initialQuadrant">
        /// The quadrant from which to start downward traversal. This must contain
        /// <paramref name="location"/> and the current thread should hold an
        /// upgradeable lock on it. This lock will be released unless this quadrant
        /// happens to be the return value.
        /// </param>
        /// <param name="location">The location for which to find a leaf quadrant.</param>
        Quadrant FindAndLockTargetLeafQuadrant(Quadrant initialQuadrant, GeoCoordinates location)
        {
            Debug.Assert(initialQuadrant.Contains(location));
            Debug.Assert(initialQuadrant.Lock.ThreadOwnsUpgradeableLock);

            Quadrant targetQuadrant = initialQuadrant;

            while (!targetQuadrant.IsLeaf)
            {
                Quadrant next = targetQuadrant.GetChildContaining(location);
                next.Lock.EnterUpgradeableLock();
                targetQuadrant.Lock.ExitUpgradeableLock();

                targetQuadrant = next;
            }

            Debug.Assert(targetQuadrant.Contains(location));

            return(targetQuadrant);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Moves the element to a new position. This will return false and do
        /// nothing if another thread is currently adjusting this element.
        /// </summary>
        /// <returns><c>true</c>, if element was moved, <c>false</c> otherwise.</returns>
        /// <param name="elementRef">Element.</param>
        /// <param name="newCoordinates">New coordinates.</param>
        public bool MoveElement(IElement elementRef, GeoCoordinates newCoordinates)
        {
            Element element = (Element)elementRef;

            if (element.IsMarkedForRemoval)
            {
                return(false);
            }

            if (!element.TrySetBusy())
            {
                return(false);
            }

            // This check has to happen after TrySetBusy() to avoid a race
            // condition with RemoveElement(). After TrySetBusy() succeeds,
            // no other move or remove operation can run on this element,
            // so we are guaranteed not to move the element after it has been
            // removed. If the element will be removed, we just abort the move.
            if (element.IsMarkedForRemoval)
            {
                element.SetNotBusy();
                return(false);
            }

            while (true)
            {
                Quadrant originalQuadrant = GetAndLockQuadrantForElement(element, QuadrantLock.LockType.Shared);

                // Catch tricky issues early.
                Debug.Assert(originalQuadrant.Lock.ThreadOwnsSharedLock);
                Debug.Assert(!originalQuadrant.IsDisconnected);
                Debug.Assert(originalQuadrant.IsLeaf);

                if (originalQuadrant.Contains(newCoordinates))
                {
                    // Case (1): new position is inside the old quadrant. Just change x and y.
                    element.Coordinates = newCoordinates;
                    originalQuadrant.Lock.ExitSharedLock();
                    break;
                }

                // Case (2): new position is inside a new quadrant. Need to move
                // to a new quadrant.

                // The root node should contain all elements, so the above if statement
                // should prevent this assert.
                Debug.Assert(originalQuadrant != rootNode);

                // We have to lock the parent to avoid deadlocking with a join
                // operation if the new quadrant is also a child of the parent.
                if (!SwitchSharedLockToExclusiveLockAndLockParent(originalQuadrant))
                {
                    // If in between releasing the shared lock and acquiring the
                    // exclusive lock the quadrant became disconnected (due to
                    // a join on its parent) or became subdivided, we have to
                    // restart the move.
                    continue;
                }

                Debug.Assert(originalQuadrant.Lock.ThreadOwnsExlusiveLock);
                Debug.Assert(originalQuadrant.Parent.Lock.ThreadOwnsSharedLock);

                // If originalQuadrant was not subdivided or joined (or was
                // subdivided and joined an equal number of times), then element
                // could not have been moved because we set it to busy.
                Debug.Assert(originalQuadrant.Data.Contains(element));

                // Traverse upward to search for the nearest ancestor that contains
                // the new position. No locking is necessary because ancestors
                // cannot be subdivided since they're not leaves, and they cannot
                // be joined because we hold a lock on a child node.
                Quadrant closestContainingAncestor = originalQuadrant.Parent;
                while (!closestContainingAncestor.Contains(newCoordinates))
                {
                    closestContainingAncestor = closestContainingAncestor.Parent;

                    // This only happens if rootNode doesn't contain the new
                    // position, but that cannot happen.
                    Debug.Assert(closestContainingAncestor != null);
                }

                // Traverse downward to target quadrant. Use locking like in an insert operation.

                // It cannot be a leaf node because it is an ancestor node.
                Debug.Assert(!closestContainingAncestor.IsLeaf);

                // Find the leaf quadrant that contains the new coordinates.
                Quadrant targetQuadrant = closestContainingAncestor.GetChildContaining(newCoordinates);
                targetQuadrant.Lock.EnterUpgradeableLock();
                targetQuadrant = FindAndLockTargetLeafQuadrant(targetQuadrant, newCoordinates);
                targetQuadrant.Lock.EnterExclusiveLock();

                // Change the element's coordinates and move it to the new quadrant.
                element.Coordinates = newCoordinates;
                originalQuadrant.Data.Remove(element);
                targetQuadrant.Data.Add(element);
                elementsToNodes[element] = targetQuadrant;

                // If the target quadrant now contains too much data, mark it as a candidate
                // for subdivision.
                if (targetQuadrant.Data.Count > maxLeafCapacity && targetQuadrant.SubdivisionLevel < maxSubdivisionLevel)
                {
                    candidatesForSubdivide.TryAdd(targetQuadrant);
                }

                // If the original quadrant is now nearly empty, mark its parent
                // as a candidate for a join operation.
                if (originalQuadrant.Parent != null && originalQuadrant.Data.Count < minLeafCapacity)
                {
                    candidatesForJoin.TryAdd(originalQuadrant.Parent);
                }

                targetQuadrant.Lock.ExitExclusiveLock();
                targetQuadrant.Lock.ExitUpgradeableLock();

                // Release the locks on the original quadrant and its parent,
                // which were acquired earlier.
                originalQuadrant.Lock.ExitExclusiveLock();
                originalQuadrant.Parent.Lock.ExitSharedLock();
                break;
            }

            element.SetNotBusy();
            return(true);
        }