@Override public ExclusiveLock load(LockDescriptor descriptor) throws Exception { return new ExclusiveLock(descriptor); } });
@Override public synchronized void unlock(UUID requestId) { if (Objects.equals(requestId, currentHolder)) { currentHolder = null; processQueue(); } }
@Override public synchronized AsyncResult<Void> waitUntilAvailable(UUID requestId) { return submit(new LockRequest(requestId, true)); }
private AsyncResult<Void> lockAsync(UUID requestId) { return lock.lock(requestId); }
@Test public void queuesAcquisitionsForHeldLocks() { lockA.lock(OTHER_REQUEST_ID); lockB.lock(OTHER_REQUEST_ID); AsyncResult<HeldLocks> acquisitions = acquire(lockA, lockB); lockA.unlock(OTHER_REQUEST_ID); assertFalse(acquisitions.isComplete()); lockB.unlock(OTHER_REQUEST_ID); assertThat(acquisitions.isCompletedSuccessfully()).isTrue(); }
@Test public void timedOutLockRequestDoesNotGetTheLock() { lockSynchronously(REQUEST_1); AsyncResult<Void> request2 = lockAsync(REQUEST_2); lock.timeout(REQUEST_2); unlock(REQUEST_1); assertThat(lock.getCurrentHolder()).isNull(); lockSynchronously(REQUEST_1); }
@Test public void resultIsTimedOutWhenTimeOutIsCalled() { lockSynchronously(REQUEST_1); AsyncResult<Void> request2 = lockAsync(REQUEST_2); lock.timeout(REQUEST_2); assertThat(request2.isTimedOut()).isTrue(); }
private AsyncResult<Void> waitUntilAvailableAsync(UUID request) { return lock.waitUntilAvailable(request); }
private void unlock(UUID requestId) { lock.unlock(requestId); }
@Test public void unlockByNonHolderNoOps() { lockSynchronously(REQUEST_1); unlock(UUID.randomUUID()); assertThat(lock.getCurrentHolder()).isEqualTo(REQUEST_1); }
@Test public void propagatesExceptionIfAsyncLockAcquisitionFails() { AsyncResult<Void> lockResult = new AsyncResult<>(); RuntimeException error = new RuntimeException("foo"); lockResult.fail(error); doReturn(lockResult).when(lockB).lock(any()); lockA.lock(OTHER_REQUEST_ID); AsyncResult<HeldLocks> acquisitions = acquire(lockA, lockB); lockA.unlock(OTHER_REQUEST_ID); assertThat(acquisitions.getError()).isEqualTo(error); }
@Test public void waitRequestIsTimedOutAfterDeadline() { lockSynchronously(REQUEST_1); AsyncResult<Void> request2 = waitUntilAvailableAsync(REQUEST_1); lock.timeout(REQUEST_1); assertThat(request2.isTimedOut()).isTrue(); }
@Test public void waitForLocksDoesNotAcquireLocks() { waitFor(lockA); verify(lockA).waitUntilAvailable(REQUEST_ID); verifyNoMoreInteractions(lockA); }
@Test public void unlockByWaiterNoOps() { lockSynchronously(REQUEST_1); AsyncResult<Void> request2 = lockAsync(REQUEST_2); unlock(REQUEST_2); assertThat(lock.getCurrentHolder()).isEqualTo(REQUEST_1); assertThat(request2.isComplete()).isFalse(); // request2 should still get the lock when it's available unlock(REQUEST_1); assertThat(request2.isCompletedSuccessfully()).isTrue(); }
private ExclusiveLock newLock() { return new ExclusiveLock(LOCK_DESCRIPTOR); }
@GuardedBy("this") private AsyncResult<Void> submit(LockRequest request) { queue.enqueue(request); processQueue(); return request.result; }
@Override public synchronized AsyncResult<Void> lock(UUID requestId) { return submit(new LockRequest(requestId, false)); }
private void lockSynchronously(UUID requestId) { lock.lock(requestId).get(); }