/** @throws Exception If test fails. */
  public void testMultiNodeLock() throws Exception {
    IgniteCache<Integer, String> cache1 = ignite1.cache(null);
    IgniteCache<Integer, String> cache2 = ignite2.cache(null);

    Lock lock1_1 = cache1.lock(1);
    Lock lock2_1 = cache2.lock(1);

    lock1_1.lock();

    try {
      assert cache1.isLocalLocked(1, false) : entries(1);
      assert cache1.isLocalLocked(1, true);

      assert cache2.isLocalLocked(1, false) : entries(1);
      assert !cache2.isLocalLocked(1, true);

      assert !lock2_1.tryLock();

      assert cache2.isLocalLocked(1, false) : entries(1);
      assert !cache2.isLocalLocked(1, true);
    } finally {
      lock1_1.unlock();

      checkUnlocked(cache1, 1);
    }

    CountDownLatch latch = new CountDownLatch(1);

    lock2_1.lock();

    try {
      assert cache2.isLocalLocked(1, false) : entries(1);
      assert cache2.isLocalLocked(1, true);

      assert cache1.isLocalLocked(1, false) : entries(1);
      assert !cache1.isLocalLocked(1, true);

      addListener(ignite1, new UnlockListener(latch, 1));

      assert !lock1_1.tryLock();

      assert cache1.isLocalLocked(1, false) : entries(1);
      assert !cache1.isLocalLocked(1, true);
    } finally {
      lock2_1.unlock();
    }

    latch.await();

    checkUnlocked(cache1, 1);
    checkUnlocked(cache2, 1);
  }
  /** @throws Exception If test failed. */
  public void testBasicLock() throws Exception {
    IgniteCache<Integer, String> cache = ignite1.cache(null);

    Lock lock = cache.lock(1);

    lock.lock();

    assert cache.isLocalLocked(1, false);
    assert cache.isLocalLocked(1, true);

    lock.unlock();

    checkUnlocked(cache, 1);
  }
  /** @throws Exception If failed. */
  public void testTwoCaches() throws Exception {
    IgniteCache<Integer, String> cache1 = ignite1.cache(CACHE1);
    IgniteCache<Integer, String> cache2 = ignite1.cache(CACHE2);

    final Integer key = primaryKey(cache1);

    Lock lock = cache1.lock(key);

    lock.lock();

    try {
      assertTrue(cache1.isLocalLocked(key, true));
      assertTrue(cache1.isLocalLocked(key, false));

      assertFalse(cache2.isLocalLocked(key, true));
      assertFalse(cache2.isLocalLocked(key, false));
    } finally {
      lock.unlock();
    }
  }
  /** @throws IgniteCheckedException If test failed. */
  public void testLockReentry() throws IgniteCheckedException {
    IgniteCache<Integer, String> cache = ignite1.cache(null);

    Lock lock = cache.lock(1);

    lock.lock();

    try {
      checkLocked(cache, 1);

      lock.lock();

      checkLocked(cache, 1);

      lock.unlock();

      checkLocked(cache, 1);
    } finally {
      lock.unlock();
    }

    checkUnlocked(cache, 1);
  }
  /** @throws Exception If test failed. */
  public void testLockMultithreaded() throws Exception {
    final IgniteCache<Integer, String> cache = ignite1.cache(null);

    final CountDownLatch l1 = new CountDownLatch(1);
    final CountDownLatch l2 = new CountDownLatch(1);

    final Lock lock1 = cache.lock(1);

    GridTestThread t1 =
        new GridTestThread(
            new Callable<Object>() {
              /** {@inheritDoc} */
              @Nullable
              @Override
              public Object call() throws Exception {
                info("Before lock for.key 1");

                lock1.lock();

                info("After lock for key 1");

                try {
                  checkLocked(cache, 1);

                  l1.countDown();

                  info("Let thread2 proceed.");

                  // Reentry.
                  lock1.lock();

                  checkLocked(cache, 1);

                  // Nested lock.
                  Lock lock2 = cache.lock(2);

                  assert lock2.tryLock();

                  checkLocked(cache, 2);

                  // Unlock reentry.
                  lock1.unlock();

                  // Outer should still be owned.
                  checkLocked(cache, 1);

                  // Unlock in reverse order.
                  lock2.unlock();

                  checkUnlocked(cache, 2);

                  l2.await();

                  info("Waited for latch 2");
                } finally {
                  lock1.unlock();

                  info("Unlocked entry for key 1.");
                }

                assert !cache.isLocalLocked(1, true);
                assert !cache.isLocalLocked(2, true);

                return null;
              }
            });

    GridTestThread t2 =
        new GridTestThread(
            new Callable<Object>() {
              /** {@inheritDoc} */
              @Nullable
              @Override
              public Object call() throws Exception {
                info("Waiting for latch1...");

                l1.await();

                info("Latch1 released.");

                assert !lock1.tryLock();

                info("Tried to lock cache for key1");

                l2.countDown();

                info("Released latch2");

                lock1.lock();

                try {
                  info("Locked cache for key 1");

                  checkLocked(cache, 1);

                  info("Checked that cache is locked for key 1");
                } finally {
                  lock1.unlock();

                  info("Unlocked cache for key 1");
                }

                checkUnlocked(cache, 1);

                return null;
              }
            });

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    t1.checkError();
    t2.checkError();

    checkUnlocked(cache, 1);
    checkUnlocked(cache, 2);
  }
  /** @throws Exception If test fails. */
  public void testMultiNodeLockWithKeyLists() throws Exception {
    IgniteCache<Integer, String> cache1 = ignite1.cache(null);
    IgniteCache<Integer, String> cache2 = ignite2.cache(null);

    Collection<Integer> keys1 = Arrays.asList(1, 2, 3);
    Collection<Integer> keys2 = Arrays.asList(2, 3, 4);

    Lock lock1_1 = cache1.lockAll(keys1);
    Lock lock2_2 = cache2.lockAll(keys2);

    lock1_1.lock();

    checkLocked(cache1, keys1);

    try {
      assert !lock2_2.tryLock();

      assert cache2.isLocalLocked(2, false);
      assert cache2.isLocalLocked(3, false);

      checkUnlocked(cache1, 4);
      checkUnlocked(cache2, 4);

      assert !cache2.isLocalLocked(2, true);
      assert !cache2.isLocalLocked(3, true);
      assert !cache2.isLocalLocked(4, true);
    } finally {
      lock1_1.unlock();
    }

    checkUnlocked(cache1, keys1);

    checkUnlocked(cache1, keys2);
    checkUnlocked(cache2, 4);

    lock2_2.lock();

    CountDownLatch latch1 = new CountDownLatch(keys2.size());
    CountDownLatch latch2 = new CountDownLatch(1);

    addListener(ignite2, new UnlockListener(latch2, 1));
    addListener(ignite1, (new UnlockListener(latch1, keys2)));

    Lock lock1_ = cache1.lock(1);

    try {
      checkLocked(cache2, keys2);

      checkUnlocked(cache2, 1);

      assert lock1_.tryLock();

      checkLocked(cache1, 1);

      checkRemoteLocked(cache1, keys2);

      checkRemoteLocked(cache2, 1);
    } finally {
      lock2_2.unlock();

      lock1_.unlock();
    }

    latch1.await();
    latch2.await();

    checkUnlocked(cache1, keys1);
    checkUnlocked(cache2, keys1);
    checkUnlocked(cache1, keys2);
    checkUnlocked(cache2, keys2);
  }