2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.cluster.access.client;
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import java.util.concurrent.CountDownLatch;
17 import java.util.concurrent.locks.StampedLock;
20 * A lock implementation which allows users to perform optimistic reads and validate them in a fashion similar
21 * to {@link StampedLock}. In case a read is contented with a write, the read side will throw
22 * an {@link InversibleLockException}, which the caller can catch and use to wait for the write to resolve.
24 public final class InversibleLock {
25 private static final VarHandle LATCH;
29 LATCH = MethodHandles.lookup().findVarHandle(InversibleLock.class, "latch", CountDownLatch.class);
30 } catch (NoSuchFieldException | IllegalAccessException e) {
31 throw new ExceptionInInitializerError(e);
35 private final StampedLock lock = new StampedLock();
37 @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
38 justification = "https://github.com/spotbugs/spotbugs/issues/2749")
39 private volatile CountDownLatch latch;
42 * Return a stamp for read validation.
44 * @return A stamp, which can be used with {@link #validate(long)}.
45 * @throws InversibleLockException if this lock is currently write-locked
47 public long optimisticRead() {
49 final long stamp = lock.tryOptimisticRead();
54 // Write-locked. Read the corresponding latch and if present report an exception, which will propagate
55 // and force release of locks.
56 final var local = latch;
58 throw new InversibleLockException(local);
61 // No latch present: retry optimistic lock
65 public boolean validate(final long stamp) {
66 return lock.validate(stamp);
69 public long writeLock() {
70 verify(LATCH.compareAndSet(this, null, new CountDownLatch(1)));
71 return lock.writeLock();
74 public void unlockWrite(final long stamp) {
75 final var local = verifyNotNull((CountDownLatch) LATCH.getAndSet(this, null));
76 lock.unlockWrite(stamp);