Beispiel #1
0
  @Override
  public void acquireShared(Locks.ResourceType resourceType, long... resourceIds)
      throws AcquireLockTimeoutException {
    ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()];
    Map<Long, Integer> heldShareLocks = sharedLockCounts[resourceType.typeId()];
    Map<Long, Integer> heldExclusiveLocks = exclusiveLockCounts[resourceType.typeId()];

    for (long resourceId : resourceIds) {
      Integer heldCount = heldShareLocks.get(resourceId);
      if (heldCount != null) {
        // We already have a lock on this, just increment our local reference counter.
        heldShareLocks.put(resourceId, heldCount + 1);
        continue;
      }

      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;
      }

      int tries = 0;
      SharedLock mySharedLock = null;
      while (true) {
        ForsetiLockManager.Lock existingLock = lockMap.get(resourceId);
        if (existingLock == null) {
          // Try to create a new shared lock
          if (mySharedLock == null) {
            mySharedLock = new SharedLock(this);
          }
          if (lockMap.putIfAbsent(resourceId, mySharedLock) == null) {
            // Success!
            break;
          } else {
            continue;
          }
        } else if (existingLock instanceof SharedLock) {
          if (((SharedLock) existingLock).acquire(this)) {
            // Success!
            break;
          }
        } else if (existingLock instanceof ExclusiveLock) {
          // We need to wait, just let the loop run.
        } else {
          throw new UnsupportedOperationException("Unknown lock type: " + existingLock);
        }

        waitStrategies[resourceType.typeId()].apply(tries++);
        markAsWaitingFor(existingLock, resourceType, resourceId);
      }

      clearWaitList();
      heldShareLocks.put(resourceId, 1);
    }
  }
Beispiel #2
0
  @Override
  public void releaseShared(Locks.ResourceType resourceType, long... resourceIds) {
    for (long resourceId : resourceIds) {
      if (releaseLocalLock(resourceType, resourceId, sharedLockCounts[resourceType.typeId()])) {
        continue;
      }

      // Only release if we were not holding an exclusive lock as well
      if (!exclusiveLockCounts[resourceType.typeId()].containsKey(resourceId)) {
        releaseGlobalLock(lockMaps[resourceType.typeId()], resourceId);
      }
    }
  }
Beispiel #3
0
  @Override
  public void releaseExclusive(Locks.ResourceType resourceType, long... resourceIds) {
    for (long resourceId : resourceIds) {
      if (releaseLocalLock(resourceType, resourceId, exclusiveLockCounts[resourceType.typeId()])) {
        continue;
      }

      if (sharedLockCounts[resourceType.typeId()].containsKey(resourceId)) {
        // We are still holding a shared lock, so swap the exclusive lock for that
        lockMaps[resourceType.typeId()].put(resourceId, new SharedLock(this));
      } else {
        releaseGlobalLock(lockMaps[resourceType.typeId()], resourceId);
      }
    }
  }
Beispiel #4
0
  // TODO These kinds of variadic APIs look generally problematic to me.
  // TODO Say we're trying to grab the locks on [1, 2, 3]. Getting the lock on 1
  // TODO succeeds, but getting the lock on 2 fails. Then we return 'false', leave
  // TODO the lock on 1 held, and never try to grab the lock on 3.
  // TODO That sounds like a mess to me. Basically, if you try to grab more than
  // TODO one lock at a time, and methods like this one returns 'false', then you
  // TODO have no idea what locks you did or did not get.
  // TODO I think the API with batched lock-grabbing should be dropped completely.
  // TODO Especially considering the implementation of Forseti, or the general
  // TODO concept of lock managers as a whole, I don't think batched lock-grabbing
  // TODO will ever give any noticable performance benefit anyway.
  @Override
  public boolean trySharedLock(Locks.ResourceType resourceType, long... resourceIds) {
    ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()];
    Map<Long, Integer> heldShareLocks = sharedLockCounts[resourceType.typeId()];
    Map<Long, Integer> heldExclusiveLocks = exclusiveLockCounts[resourceType.typeId()];

    for (long resourceId : resourceIds) {
      Integer heldCount = heldShareLocks.get(resourceId);
      if (heldCount != null) {
        // We already have a lock on this, just increment our local reference counter.
        heldShareLocks.put(resourceId, heldCount + 1);
        continue;
      }

      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;
      }

      while (true) {
        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 instanceof SharedLock) {
          if (((SharedLock) existingLock).acquire(this)) {
            // Success!
            break;
          } else {
            return false;
          }
        } else if (existingLock instanceof ExclusiveLock) {
          return false;
        } else {
          throw new UnsupportedOperationException("Unknown lock type: " + existingLock);
        }
      }
      heldShareLocks.put(resourceId, 1);
    }
    return true;
  }
Beispiel #5
0
  /** 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;
  }
Beispiel #6
0
  /**
   * 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);
    }
  }
Beispiel #7
0
 @Override
 public void write(ChannelBuffer buffer) throws IOException {
   buffer.writeInt(type.typeId());
   buffer.writeInt(resourceIds.length);
   for (long entity : resourceIds) {
     buffer.writeLong(entity);
   }
 }
Beispiel #8
0
  @Override
  public void acquireExclusive(Locks.ResourceType resourceType, long... resourceIds)
      throws AcquireLockTimeoutException {
    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 existingLock;
      int tries = 0;
      while ((existingLock = lockMap.putIfAbsent(resourceId, myExclusiveLock)) != null) {
        // 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 instanceof SharedLock) {
          // Then we should upgrade that lock
          SharedLock sharedLock = (SharedLock) existingLock;
          if (tryUpgradeSharedToExclusive(resourceType, lockMap, resourceId, sharedLock)) {
            break;
          }
        }

        waitStrategies[resourceType.typeId()].apply(tries++);
        markAsWaitingFor(existingLock, resourceType, resourceId);
      }

      clearWaitList();
      heldLocks.put(resourceId, 1);
    }
  }
Beispiel #9
0
  @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;
  }