/** Attempt to upgrade a share lock that we hold to an exclusive lock. */ private boolean tryUpgradeToExclusiveWithShareLockHeld( Locks.ResourceType resourceType, ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap, long resourceId, SharedLock sharedLock, int tries) throws AcquireLockTimeoutException { if (sharedLock.tryAcquireUpdateLock(this)) { try { // Now we just wait for all clients to release the the share lock while (sharedLock.numberOfHolders() > 1) { waitStrategies[resourceType.typeId()].apply(tries++); markAsWaitingFor(sharedLock, resourceType, resourceId); } // No more people other than us holding this lock. Swap it to exclusive // TODO Wait, why do we need to do this? An update lock with zero shared holders is an // TODO exclusive lock, no? Why is it not enough to just atomically raise the update bit, // TODO and then wait for all the shared holders to relinquish their grasp? lockMap.put(resourceId, myExclusiveLock); return true; } catch (DeadlockDetectedException e) { sharedLock.releaseUpdateLock(this); throw e; } catch (Throwable e) { sharedLock.releaseUpdateLock(this); clearWaitList(); throw new RuntimeException(e); } } return false; }
/** * Attempt to upgrade a share lock to an exclusive lock, grabbing the share lock if we don't hold * it. */ private boolean tryUpgradeSharedToExclusive( Locks.ResourceType resourceType, ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap, long resourceId, SharedLock sharedLock) throws AcquireLockTimeoutException { int tries = 0; if (!sharedLockCounts[resourceType.typeId()].containsKey(resourceId)) { // We don't hold the shared lock, we need to grab it to upgrade it to an exclusive one if (!sharedLock.acquire(this)) { return false; } try { if (tryUpgradeToExclusiveWithShareLockHeld( resourceType, lockMap, resourceId, sharedLock, tries)) { return true; } else { releaseGlobalLock(lockMap, resourceId); return false; } } catch (Throwable e) { releaseGlobalLock(lockMap, resourceId); throw e; } } else { // We do hold the shared lock, so no reason to deal with the complexity in the case above. return tryUpgradeToExclusiveWithShareLockHeld( resourceType, lockMap, resourceId, sharedLock, tries); } }
/** * Atomically increment a counter in the ControllerBB sharedMap. * * @param keyBase A string from ControllerBB that prefixes a vm id to use as a key in the * ControllerBB shared map. The VM id used is the current vm's id. */ public static void incMapCounter(String keyBase) { String key = keyBase + RemoteTestModule.getMyVmid(); Log.getLogWriter().info("Incrementing sharedMap counter " + key); SharedLock slock = ControllerBB.getBB().getSharedLock(); slock.lock(); try { int newValue = 0; Object value = ControllerBB.getBB().getSharedMap().get(key); if (value == null) { newValue = 1; } else { newValue = ((Integer) value).intValue() + 1; } ControllerBB.getBB().getSharedMap().put(key, new Integer(newValue)); Log.getLogWriter().info("Incremented sharedMap counter, count is now " + newValue); } finally { slock.unlock(); } }
@Override public boolean tryExclusiveLock(Locks.ResourceType resourceType, long... resourceIds) { ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()]; Map<Long, Integer> heldLocks = exclusiveLockCounts[resourceType.typeId()]; for (long resourceId : resourceIds) { Integer heldCount = heldLocks.get(resourceId); if (heldCount != null) { // We already have a lock on this, just increment our local reference counter. heldLocks.put(resourceId, heldCount + 1); continue; } // Grab the global lock ForsetiLockManager.Lock lock; if ((lock = lockMap.putIfAbsent(resourceId, myExclusiveLock)) != null) { if (lock instanceof SharedLock && sharedLockCounts[resourceType.typeId()].containsKey(resourceId)) { SharedLock sharedLock = (SharedLock) lock; if (sharedLock.tryAcquireUpdateLock(this)) { if (sharedLock.numberOfHolders() == 1) { lockMap.put(resourceId, myExclusiveLock); return true; } else { sharedLock.releaseUpdateLock(this); return false; } } } return false; } heldLocks.put(resourceId, 1); } return true; }