private bool IsDeadlockReal(ForsetiLockManager.Lock @lock, int tries) { ISet <ForsetiLockManager.Lock> waitedUpon = new HashSet <ForsetiLockManager.Lock>(); ISet <ForsetiClient> owners = new HashSet <ForsetiClient>(); ISet <ForsetiLockManager.Lock> nextWaitedUpon = new HashSet <ForsetiLockManager.Lock>(); ISet <ForsetiClient> nextOwners = new HashSet <ForsetiClient>(); @lock.CollectOwners(owners); do { waitedUpon.addAll(nextWaitedUpon); CollectNextOwners(waitedUpon, owners, nextWaitedUpon, nextOwners); if (nextOwners.Contains(this) && tries > 20) { // Worrying... let's take a deep breath nextOwners.Clear(); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10)); // ... and check again CollectNextOwners(waitedUpon, owners, nextWaitedUpon, nextOwners); if (nextOwners.Contains(this)) { // Yes, this deadlock looks real. return(true); } } owners.Clear(); ISet <ForsetiClient> ownersTmp = owners; owners = nextOwners; nextOwners = ownersTmp; } while (nextWaitedUpon.Count > 0); // Nope, we didn't find any real wait cycles. return(false); }
public override void ReleaseExclusive(ResourceType resourceType, params long[] resourceIds) { _stateHolder.incrementActiveClients(this); try { ConcurrentMap <long, ForsetiLockManager.Lock> resourceTypeLocks = _lockMaps[resourceType.TypeId()]; MutableLongIntMap exclusiveLocks = _exclusiveLockCounts[resourceType.TypeId()]; MutableLongIntMap sharedLocks = _sharedLockCounts[resourceType.TypeId()]; foreach (long resourceId in resourceIds) { if (ReleaseLocalLock(resourceType, resourceId, exclusiveLocks)) { continue; } if (sharedLocks.containsKey(resourceId)) { // We are still holding a shared lock, so we will release it to be reused ForsetiLockManager.Lock @lock = resourceTypeLocks.get(resourceId); if (@lock is SharedLock) { SharedLock sharedLock = ( SharedLock )@lock; if (sharedLock.UpdateLock) { sharedLock.ReleaseUpdateLock(); } else { throw new System.InvalidOperationException("Incorrect state of exclusive lock. Lock should be updated " + "to exclusive before attempt to release it. Lock: " + this); } } else { // in case if current lock is exclusive we swap it to new shared lock SharedLock sharedLock = new SharedLock(this); resourceTypeLocks.put(resourceId, sharedLock); } } else { // we do not hold shared lock so we just releasing it ReleaseGlobalLock(resourceTypeLocks, resourceId); } } } finally { _stateHolder.decrementActiveClients(); } }
/// <summary> /// Release a lock from the global pool. </summary> private void ReleaseGlobalLock(ConcurrentMap <long, ForsetiLockManager.Lock> lockMap, long resourceId) { ForsetiLockManager.Lock @lock = lockMap.get(resourceId); if (@lock is ExclusiveLock) { lockMap.remove(resourceId); } else if (@lock is SharedLock && (( SharedLock )@lock).Release(this)) { // We were the last to hold this lock, it is now dead and we should remove it. // Also cleaning updater reference that can hold lock in memory (( SharedLock )@lock).CleanUpdateHolder(); lockMap.remove(resourceId); } }
private void CollectNextOwners(ISet <ForsetiLockManager.Lock> waitedUpon, ISet <ForsetiClient> owners, ISet <ForsetiLockManager.Lock> nextWaitedUpon, ISet <ForsetiClient> nextOwners) { nextWaitedUpon.Clear(); foreach (ForsetiClient owner in owners) { ForsetiLockManager.Lock waitingForLock = owner._waitingForLock; if (waitingForLock != null && !waitedUpon.Contains(waitingForLock)) { nextWaitedUpon.Add(waitingForLock); } } foreach (ForsetiLockManager.Lock lck in nextWaitedUpon) { lck.CollectOwners(nextOwners); } }
/// <summary> /// Attempt to upgrade a share lock that we hold to an exclusive lock. </summary> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: private boolean tryUpgradeToExclusiveWithShareLockHeld(org.neo4j.storageengine.api.lock.LockTracer tracer, org.neo4j.storageengine.api.lock.LockWaitEvent priorEvent, org.neo4j.storageengine.api.lock.ResourceType resourceType, long resourceId, SharedLock sharedLock, int tries, long waitStartMillis) throws org.neo4j.storageengine.api.lock.AcquireLockTimeoutException private bool TryUpgradeToExclusiveWithShareLockHeld(LockTracer tracer, LockWaitEvent priorEvent, ResourceType resourceType, long resourceId, SharedLock sharedLock, int tries, long waitStartMillis) { if (sharedLock.TryAcquireUpdateLock(this)) { LockWaitEvent waitEvent = null; try { // Now we just wait for all clients to release the the share lock while (sharedLock.NumberOfHolders() > 1) { AssertValid(waitStartMillis, resourceType, resourceId); if (waitEvent == null && priorEvent == null) { waitEvent = tracer.WaitForLock(true, resourceType, resourceId); } WaitFor(sharedLock, resourceType, resourceId, true, tries++); } return(true); } catch (Exception e) { sharedLock.ReleaseUpdateLock(); if (e is DeadlockDetectedException || e is LockClientStoppedException) { throw ( Exception )e; } throw new TransactionFailureException("Failed to upgrade shared lock to exclusive: " + sharedLock, e); } finally { if (waitEvent != null) { waitEvent.Close(); } ClearWaitList(); _waitingForLock = null; } } return(false); }
private void WaitFor(ForsetiLockManager.Lock @lock, ResourceType type, long resourceId, bool exclusive, int tries) { _waitingForLock = @lock; ClearAndCopyWaitList(@lock); _waitStrategies[type.TypeId()].apply(tries); int b = @lock.DetectDeadlock(Id()); if (b != -1 && _deadlockResolutionStrategy.shouldAbort(this, _clientById.apply(b))) { // Force the operations below to happen after the reads we do for deadlock // detection in the lines above, as a way to cut down on false-positive deadlocks UnsafeUtil.loadFence(); // Create message before we clear the wait-list, to lower the chance of the message being insane string message = this + " can't acquire " + @lock + " on " + type + "(" + resourceId + "), because holders of that lock " + "are waiting for " + this + ".\n Wait list:" + @lock.DescribeWaitList(); // Minimize the risk of false positives by double-checking that the deadlock remains // after we've generated a description of it. if (@lock.DetectDeadlock(Id()) != -1) { // If the deadlock is real, then an owner of this lock must be (transitively) waiting on a lock that // we own. So to verify the deadlock, we traverse the lock owners and their `waitingForLock` fields, // to find a lock that has us among the owners. // We only act upon the result of this method if the `tries` count is above some threshold. The reason // is that the Lock.collectOwners, which is algorithm relies upon, is inherently racy, and so only // reduces the probably of a false positive, but does not eliminate them. if (IsDeadlockReal(@lock, tries)) { // After checking several times, this really does look like a real deadlock. throw new DeadlockDetectedException(message); } } } }
private void ClearAndCopyWaitList(ForsetiLockManager.Lock @lock) { ClearWaitList(); @lock.CopyHolderWaitListsInto(_waitList); }
public override bool TrySharedLock(ResourceType resourceType, long resourceId) { _hasLocks = true; _stateHolder.incrementActiveClients(this); try { ConcurrentMap <long, ForsetiLockManager.Lock> lockMap = _lockMaps[resourceType.TypeId()]; MutableLongIntMap heldShareLocks = _sharedLockCounts[resourceType.TypeId()]; MutableLongIntMap heldExclusiveLocks = _exclusiveLockCounts[resourceType.TypeId()]; int heldCount = heldShareLocks.getIfAbsent(resourceId, -1); if (heldCount != -1) { // We already have a lock on this, just increment our local reference counter. heldShareLocks.put(resourceId, Math.incrementExact(heldCount)); return(true); } if (heldExclusiveLocks.containsKey(resourceId)) { // We already have an exclusive lock, so just leave that in place. When the exclusive lock is released, // it will be automatically downgraded to a shared lock, since we bumped the share lock reference count. heldShareLocks.put(resourceId, 1); return(true); } long waitStartMillis = _clock.millis(); while (true) { AssertValid(waitStartMillis, resourceType, resourceId); ForsetiLockManager.Lock existingLock = lockMap.get(resourceId); if (existingLock == null) { // Try to create a new shared lock if (lockMap.putIfAbsent(resourceId, new SharedLock(this)) == null) { // Success! break; } } else if (existingLock is SharedLock) { // Note that there is a "safe" race here where someone may be releasing the last reference to a lock // and thus removing that lock instance (making it unacquirable). In this case, we allow retrying, // even though this is a try-lock call. if ((( SharedLock )existingLock).Acquire(this)) { // Success! break; } else if ((( SharedLock )existingLock).UpdateLock) { return(false); } } else if (existingLock is ExclusiveLock) { return(false); } else { throw new System.NotSupportedException("Unknown lock type: " + existingLock); } } heldShareLocks.put(resourceId, 1); return(true); } finally { _stateHolder.decrementActiveClients(); } }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: public void acquireExclusive(org.neo4j.storageengine.api.lock.LockTracer tracer, org.neo4j.storageengine.api.lock.ResourceType resourceType, long... resourceIds) throws org.neo4j.storageengine.api.lock.AcquireLockTimeoutException public override void AcquireExclusive(LockTracer tracer, ResourceType resourceType, params long[] resourceIds) { _hasLocks = true; _stateHolder.incrementActiveClients(this); LockWaitEvent waitEvent = null; try { ConcurrentMap <long, ForsetiLockManager.Lock> lockMap = _lockMaps[resourceType.TypeId()]; MutableLongIntMap heldLocks = _exclusiveLockCounts[resourceType.TypeId()]; foreach (long resourceId in resourceIds) { int heldCount = heldLocks.getIfAbsent(resourceId, -1); if (heldCount != -1) { // We already have a lock on this, just increment our local reference counter. heldLocks.put(resourceId, Math.incrementExact(heldCount)); continue; } // Grab the global lock ForsetiLockManager.Lock existingLock; int tries = 0; long waitStartMillis = _clock.millis(); while ((existingLock = lockMap.putIfAbsent(resourceId, _myExclusiveLock)) != null) { AssertValid(waitStartMillis, resourceType, resourceId); // If this is a shared lock: // Given a grace period of tries (to try and not starve readers), grab an update lock and wait // for it to convert to an exclusive lock. if (tries > 50 && existingLock is SharedLock) { // Then we should upgrade that lock SharedLock sharedLock = ( SharedLock )existingLock; if (TryUpgradeSharedToExclusive(tracer, waitEvent, resourceType, lockMap, resourceId, sharedLock, waitStartMillis)) { break; } } if (waitEvent == null) { waitEvent = tracer.WaitForLock(true, resourceType, resourceId); } WaitFor(existingLock, resourceType, resourceId, true, tries++); } heldLocks.put(resourceId, 1); } } finally { if (waitEvent != null) { waitEvent.Close(); } ClearWaitList(); _waitingForLock = null; _stateHolder.decrementActiveClients(); } }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#: //ORIGINAL LINE: public void acquireShared(org.neo4j.storageengine.api.lock.LockTracer tracer, org.neo4j.storageengine.api.lock.ResourceType resourceType, long... resourceIds) throws org.neo4j.storageengine.api.lock.AcquireLockTimeoutException public override void AcquireShared(LockTracer tracer, ResourceType resourceType, params long[] resourceIds) { _hasLocks = true; _stateHolder.incrementActiveClients(this); LockWaitEvent waitEvent = null; try { // Grab the global lock map we will be using ConcurrentMap <long, ForsetiLockManager.Lock> lockMap = _lockMaps[resourceType.TypeId()]; // And grab our local lock maps MutableLongIntMap heldShareLocks = _sharedLockCounts[resourceType.TypeId()]; MutableLongIntMap heldExclusiveLocks = _exclusiveLockCounts[resourceType.TypeId()]; foreach (long resourceId in resourceIds) { // First, check if we already hold this as a shared lock int heldCount = heldShareLocks.getIfAbsent(resourceId, -1); if (heldCount != -1) { // We already have a lock on this, just increment our local reference counter. heldShareLocks.put(resourceId, Math.incrementExact(heldCount)); continue; } // Second, check if we hold it as an exclusive lock if (heldExclusiveLocks.containsKey(resourceId)) { // We already have an exclusive lock, so just leave that in place. // When the exclusive lock is released, it will be automatically downgraded to a shared lock, // since we bumped the share lock reference count. heldShareLocks.put(resourceId, 1); continue; } // We don't hold the lock, so we need to grab it via the global lock map int tries = 0; SharedLock mySharedLock = null; long waitStartMillis = _clock.millis(); // Retry loop while (true) { AssertValid(waitStartMillis, resourceType, resourceId); // Check if there is a lock for this entity in the map ForsetiLockManager.Lock existingLock = lockMap.get(resourceId); // No lock if (existingLock == null) { // Try to create a new shared lock if (mySharedLock == null) { mySharedLock = new SharedLock(this); } if (lockMap.putIfAbsent(resourceId, mySharedLock) == null) { // Success, we now hold the shared lock. break; } else { continue; } } // Someone holds shared lock on this entity, try and get in on that action else if (existingLock is SharedLock) { if ((( SharedLock )existingLock).Acquire(this)) { // Success! break; } } // Someone holds an exclusive lock on this entity else if (existingLock is ExclusiveLock) { // We need to wait, just let the loop run. } else { throw new System.NotSupportedException("Unknown lock type: " + existingLock); } if (waitEvent == null) { waitEvent = tracer.WaitForLock(false, resourceType, resourceId); } // And take note of who we are waiting for. This is used for deadlock detection. WaitFor(existingLock, resourceType, resourceId, false, tries++); } // Make a local note about the fact that we now hold this lock heldShareLocks.put(resourceId, 1); } } finally { if (waitEvent != null) { waitEvent.Close(); } ClearWaitList(); _waitingForLock = null; _stateHolder.decrementActiveClients(); } }