7e684453969e3702af4b5027730b51ed946ab2f7
[controller.git] / opendaylight / md-sal / cds-access-client / src / main / java / org / opendaylight / controller / cluster / access / client / InversibleLock.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.access.client;
9
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12
13 import java.lang.invoke.MethodHandles;
14 import java.lang.invoke.VarHandle;
15 import java.util.concurrent.CountDownLatch;
16 import java.util.concurrent.locks.StampedLock;
17
18 /**
19  * A lock implementation which allows users to perform optimistic reads and validate them in a fashion similar
20  * to {@link StampedLock}. In case a read is contented with a write, the read side will throw
21  * an {@link InversibleLockException}, which the caller can catch and use to wait for the write to resolve.
22  */
23 public final class InversibleLock {
24     private static final VarHandle LATCH;
25
26     static {
27         try {
28             LATCH = MethodHandles.lookup().findVarHandle(InversibleLock.class, "latch", CountDownLatch.class);
29         } catch (NoSuchFieldException | IllegalAccessException e) {
30             throw new ExceptionInInitializerError(e);
31         }
32     }
33
34     private final StampedLock lock = new StampedLock();
35
36     private volatile CountDownLatch latch;
37
38     /**
39      * Return a stamp for read validation.
40      *
41      * @return A stamp, which can be used with {@link #validate(long)}.
42      * @throws InversibleLockException if this lock is currently write-locked
43      */
44     public long optimisticRead() {
45         while (true) {
46             final long stamp = lock.tryOptimisticRead();
47             if (stamp != 0) {
48                 return stamp;
49             }
50
51             // Write-locked. Read the corresponding latch and if present report an exception, which will propagate
52             // and force release of locks.
53             final var local = latch;
54             if (local != null) {
55                 throw new InversibleLockException(local);
56             }
57
58             // No latch present: retry optimistic lock
59         }
60     }
61
62     public boolean validate(final long stamp) {
63         return lock.validate(stamp);
64     }
65
66     public long writeLock() {
67         verify(LATCH.compareAndSet(this, null, new CountDownLatch(1)));
68         return lock.writeLock();
69     }
70
71     public void unlockWrite(final long stamp) {
72         final CountDownLatch local = (CountDownLatch) LATCH.getAndSet(this, null);
73         verifyNotNull(local);
74         lock.unlockWrite(stamp);
75         local.countDown();
76     }
77 }