@Test
  public void testReleaseOrphanedGrant_Local() throws Exception {
    DLockRequestProcessor.setDebugReleaseOrphanedGrant(true);
    DLockRequestProcessor.setWaitToProcessDLockResponse(false);
    try {
      startedThread2_testReleaseOrphanedGrant = false;
      gotLockThread2_testReleaseOrphanedGrant = false;
      releaseThread1_testReleaseOrphanedGrant = false;

      logger.info("[testReleaseOrphanedGrant_Local] create lock service");
      final String serviceName = getUniqueName();
      final DistributedLockService service = DistributedLockService.create(serviceName, dlstSystem);

      // thread to get lock and wait and then unlock
      final Thread thread1 = new Thread(() -> {
        logger.info("[testReleaseOrphanedGrant_Local] get the lock");
        assertThat(service.lock("obj", -1, -1)).isTrue();
        DLockRequestProcessor.setWaitToProcessDLockResponse(true);
        startedThread1_testReleaseOrphanedGrant = true;
        synchronized (Thread.currentThread()) {
          while (!releaseThread1_testReleaseOrphanedGrant) {
            try {
              Thread.currentThread().wait();
            } catch (InterruptedException ignore) {
              fail("interrupted");
            }
          }
        }
        logger.info("[testReleaseOrphanedGrant_Local] unlock the lock");
        service.unlock("obj");
      });
      thread1.start();
      while (!startedThread1_testReleaseOrphanedGrant) {
        Thread.yield();
      }

      // thread to interrupt lockInterruptibly call to cause zombie grant
      final Thread thread2 = new Thread(() -> {
        try {
          logger
              .info("[testReleaseOrphanedGrant_Local] call lockInterruptibly");
          startedThread2_testReleaseOrphanedGrant = true;
          assertThat(service.lockInterruptibly("obj", -1, -1)).isFalse();
        } catch (InterruptedException expected) {
          Thread.currentThread().interrupt();
        }
      });
      thread2.start();
      while (!startedThread2_testReleaseOrphanedGrant) {
        Thread.yield();
      }

      // release first thread to unlock
      logger.info("[testReleaseOrphanedGrant_Local] release 1st thread");
      Thread.sleep(500);
      synchronized (thread1) {
        releaseThread1_testReleaseOrphanedGrant = true;
        thread1.notifyAll();
      }
      Thread.sleep(500);

      // while first thread is stuck on waitToProcessDLockResponse,
      // interrupt 2nd thread
      logger.info("[testReleaseOrphanedGrant_Local] interrupt 2nd thread");
      thread2.interrupt();
      ThreadUtils.join(thread2, 20 * 1000);

      // release waitToProcessDLockResponse
      logger.info("[testReleaseOrphanedGrant_Local] process lock response");
      Thread.sleep(500);
      DLockRequestProcessor.setWaitToProcessDLockResponse(false);

      // relock obj to make sure zombie release worked
      logger.info("[testReleaseOrphanedGrant_Local] verify lock not held");
      assertThat(service.lock("obj", 1000, -1)).isTrue();
    } finally {
      DLockRequestProcessor.setDebugReleaseOrphanedGrant(false);
      DLockRequestProcessor.setWaitToProcessDLockResponse(false);
    }
  }
Client requests lock and then interrupts lock request before processing the grant reply. This
causes the Client to send a release msg to the grantor.
geode-core/src/distributedTest/java/org/apache/geode/distributed/DistributedLockServiceDUnitTest.java