Bump upstreams
[bgpcep.git] / pcep / topology / topology-provider / src / main / java / org / opendaylight / bgpcep / pcep / topology / provider / PCEPRequest.java
1 /*
2  * Copyright (c) 2013 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.bgpcep.pcep.topology.provider;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import com.google.common.util.concurrent.SettableFuture;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import io.netty.util.Timeout;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import java.util.concurrent.TimeUnit;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.OperationResult;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.lsp.metadata.Metadata;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 final class PCEPRequest {
23     /**
24      * Logical state of a {@link PCEPRequest}.
25      */
26     enum State {
27         /**
28          * The request has not been written out to the session.
29          */
30         UNSENT,
31         /**
32          * The request has been sent to to the sesssion, but has not been acknowledged by the peer.
33          */
34         UNACKED,
35         /**
36          * The request has been completed.
37          */
38         DONE,
39     }
40
41     private static final Logger LOG = LoggerFactory.getLogger(PCEPRequest.class);
42     private static final VarHandle STATE;
43
44     static {
45         try {
46             STATE = MethodHandles.lookup().findVarHandle(PCEPRequest.class, "state", State.class);
47         } catch (NoSuchFieldException | IllegalAccessException e) {
48             throw new ExceptionInInitializerError(e);
49         }
50     }
51
52     private final SettableFuture<OperationResult> future = SettableFuture.create();
53     private final long startNanos = System.nanoTime();
54     private final Metadata metadata;
55
56     // Manipulated via STATE
57     @SuppressWarnings("unused")
58     @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749")
59     private volatile State state = State.UNSENT;
60
61     // Guarded by state going to State.DONE
62     private Timeout timeout;
63
64     PCEPRequest(final Metadata metadata, final Timeout timeout) {
65         this.metadata = metadata;
66         this.timeout = timeout;
67     }
68
69     protected ListenableFuture<OperationResult> getFuture() {
70         return future;
71     }
72
73     public Metadata getMetadata() {
74         return metadata;
75     }
76
77     long getElapsedMillis() {
78         final long elapsedNanos = System.nanoTime() - startNanos;
79         final long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos);
80
81         // FIXME: this is weird: it scales (0,1) up to 1, but otherwise scales down
82         return elapsedMillis == 0 && elapsedNanos > 0 ? 1 : elapsedMillis;
83     }
84
85     /**
86      * Mark this request as {@link State#UNACKED} if it currently is {@link State#UNSENT}.
87      */
88     void markUnacked() {
89         if (STATE.compareAndSet(this, State.UNSENT, State.UNACKED)) {
90             LOG.debug("Request went from {} to {}", State.UNSENT, State.UNACKED);
91         }
92     }
93
94     /**
95      * Mark this request as {@link State#DONE} with specified {@link OperationResult}. If it is already done, this
96      * method does nothing.
97      *
98      * @param result Result to report
99      */
100     void finish(final OperationResult result) {
101         final var prev = setDone();
102         if (prev != State.DONE) {
103             setFuture(prev, result);
104         }
105     }
106
107     /**
108      * Mark this request as {@link State#DONE} with a result derived from its current state. If it is already done, this
109      * method does nothing.
110      *
111      * @return Previous state
112      */
113     State cancel() {
114         final var prev = setDone();
115         // FIXME: exhaustive when we have JDK17+
116         switch (prev) {
117             case UNSENT:
118                 setFuture(prev, OperationResults.UNSENT);
119                 break;
120             case UNACKED:
121                 setFuture(prev, OperationResults.NOACK);
122                 break;
123             case DONE:
124                 // No-op
125                 break;
126             default:
127                 throw new IllegalStateException("Unhandled state " + prev);
128         }
129         return prev;
130     }
131
132     private State setDone() {
133         return (State) STATE.getAndSet(this, State.DONE);
134     }
135
136     private void setFuture(final State prev, final OperationResult result) {
137         LOG.debug("Request went from {} to {}", prev, State.DONE);
138         if (timeout != null) {
139             timeout.cancel();
140             timeout = null;
141         }
142         future.set(result);
143     }
144 }