public static CompletionStage<Void> lockAsync(
            InterProcessLock lock, long timeout, TimeUnit unit, Executor executor) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        if (executor == null) {
            CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit));
        } else {
            CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit), executor);
        }
        return future;
    }
@Test
    public void testContention() throws Exception {
        try (CuratorFramework client =
                CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) {
            client.start();

            InterProcessMutex lock1 = new InterProcessMutex(client, "/one/two");
            InterProcessMutex lock2 = new InterProcessMutex(client, "/one/two");
            CountDownLatch latch = new CountDownLatch(1);
            AsyncWrappers.lockAsync(lock1).thenAccept(__ -> {
                latch.countDown(); // don't release the lock
            });
            assertTrue(timing.awaitLatch(latch));

            CountDownLatch latch2 = new CountDownLatch(1);
            AsyncWrappers.lockAsync(lock2, timing.forSleepingABit().milliseconds(), TimeUnit.MILLISECONDS)
                    .exceptionally(e -> {
                        if (e instanceof AsyncWrappers.TimeoutException) {
                            latch2.countDown(); // lock should still be held
                        }
                        return null;
                    });
            assertTrue(timing.awaitLatch(latch2));
        }
    }