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