Improve segmented journal actor metrics
[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 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;
18
19 /**
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.
23  */
24 public final class InversibleLock {
25     private static final VarHandle LATCH;
26
27     static {
28         try {
29             LATCH = MethodHandles.lookup().findVarHandle(InversibleLock.class, "latch", CountDownLatch.class);
30         } catch (NoSuchFieldException | IllegalAccessException e) {
31             throw new ExceptionInInitializerError(e);
32         }
33     }
34
35     private final StampedLock lock = new StampedLock();
36
37     @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
38         justification = "https://github.com/spotbugs/spotbugs/issues/2749")
39     private volatile CountDownLatch latch;
40
41     /**
42      * Return a stamp for read validation.
43      *
44      * @return A stamp, which can be used with {@link #validate(long)}.
45      * @throws InversibleLockException if this lock is currently write-locked
46      */
47     public long optimisticRead() {
48         while (true) {
49             final long stamp = lock.tryOptimisticRead();
50             if (stamp != 0) {
51                 return stamp;
52             }
53
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;
57             if (local != null) {
58                 throw new InversibleLockException(local);
59             }
60
61             // No latch present: retry optimistic lock
62         }
63     }
64
65     public boolean validate(final long stamp) {
66         return lock.validate(stamp);
67     }
68
69     public long writeLock() {
70         verify(LATCH.compareAndSet(this, null, new CountDownLatch(1)));
71         return lock.writeLock();
72     }
73
74     public void unlockWrite(final long stamp) {
75         final var local = verifyNotNull((CountDownLatch) LATCH.getAndSet(this, null));
76         lock.unlockWrite(stamp);
77         local.countDown();
78     }
79 }