096a2d973c70f7e676d938416e285acd59ffffe8
[bgpcep.git] / programming / impl / src / main / java / org / opendaylight / bgpcep / programming / impl / InstructionImpl.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.programming.impl;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import com.google.common.util.concurrent.SettableFuture;
14
15 import io.netty.util.Timeout;
16
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.List;
20
21 import javax.annotation.concurrent.GuardedBy;
22
23 import org.opendaylight.bgpcep.programming.spi.ExecutionResult;
24 import org.opendaylight.bgpcep.programming.spi.Instruction;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.CancelFailure;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.InstructionStatus;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.UncancellableInstruction;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.Details;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev130930.instruction.status.changed.DetailsBuilder;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 final class InstructionImpl implements Instruction {
35     private static final Logger LOG = LoggerFactory.getLogger(InstructionImpl.class);
36     private final List<InstructionImpl> dependants = new ArrayList<>();
37     private final SettableFuture<Instruction> schedulingFuture;
38     private final List<InstructionImpl> dependencies;
39     private final QueueInstruction queue;
40     private final InstructionId id;
41     private SettableFuture<ExecutionResult<Details>> executionFuture;
42     private InstructionStatus status = InstructionStatus.Queued;
43     private Details heldUpDetails;
44     private Timeout timeout;
45
46     InstructionImpl(final QueueInstruction queue, final SettableFuture<Instruction> future, final InstructionId id,
47             final List<InstructionImpl> dependencies, final Timeout timeout) {
48         this.schedulingFuture = Preconditions.checkNotNull(future);
49         this.dependencies = Preconditions.checkNotNull(dependencies);
50         this.timeout = Preconditions.checkNotNull(timeout);
51         this.queue = Preconditions.checkNotNull(queue);
52         this.id = Preconditions.checkNotNull(id);
53     }
54
55     InstructionId getId() {
56         return id;
57     }
58
59     InstructionStatus getStatus() {
60         return status;
61     }
62
63     synchronized void setStatus(final InstructionStatus status, final Details details) {
64         // Set the status
65         this.status = status;
66         LOG.debug("Instruction {} transitioned to status {}", id, status);
67
68         // Send out a notification
69         this.queue.instructionUpdated(status, details);
70
71         switch (status) {
72         case Cancelled:
73         case Failed:
74         case Unknown:
75             cancelDependants();
76             break;
77         case Executing:
78         case Queued:
79         case Scheduled:
80         case Successful:
81             break;
82         }
83     }
84
85     @GuardedBy("this")
86     private void cancelTimeout() {
87         if (timeout != null) {
88             timeout.cancel();
89             timeout = null;
90         }
91     }
92
93     public synchronized void timeout() {
94         if (timeout != null) {
95             timeout = null;
96
97             switch (status) {
98             case Cancelled:
99             case Failed:
100             case Successful:
101                 LOG.debug("Instruction {} has status {}, timeout is a no-op", id, status);
102                 break;
103             case Unknown:
104                 LOG.warn("Instruction {} has status {} before timeout completed", id, status);
105                 break;
106             case Executing:
107                 LOG.info("Instruction {} timed out while executing, transitioning into Unknown", id);
108                 setStatus(InstructionStatus.Unknown, null);
109                 cancelDependants();
110                 break;
111             case Queued:
112                 LOG.debug("Instruction {} timed out while Queued, cancelling it", id);
113
114                 final List<InstructionId> ids = new ArrayList<>();
115                 for (final InstructionImpl d : dependencies) {
116                     if (d.getStatus() != InstructionStatus.Successful) {
117                         ids.add(d.getId());
118                     }
119                 }
120
121                 cancel(new DetailsBuilder().setUnmetDependencies(ids).build());
122                 break;
123             case Scheduled:
124                 LOG.debug("Instruction {} timed out while Scheduled, cancelling it", id);
125                 cancel(heldUpDetails);
126                 break;
127             }
128         }
129     }
130
131     @GuardedBy("this")
132     private void cancelDependants() {
133         final Details details = new DetailsBuilder().setUnmetDependencies(ImmutableList.of(id)).build();
134         for (final InstructionImpl d : dependants) {
135             d.tryCancel(details);
136         }
137     }
138
139     @GuardedBy("this")
140     private void cancel(final Details details) {
141         cancelTimeout();
142         schedulingFuture.cancel(false);
143         setStatus(InstructionStatus.Cancelled, details);
144     }
145
146     synchronized Class<? extends CancelFailure> tryCancel(final Details details) {
147         switch (status) {
148         case Cancelled:
149         case Executing:
150         case Failed:
151         case Successful:
152         case Unknown:
153             LOG.debug("Instruction {} can no longer be cancelled due to status {}", id, status);
154             return UncancellableInstruction.class;
155         case Queued:
156         case Scheduled:
157             cancel(details);
158             return null;
159         }
160
161         throw new IllegalStateException("Unhandled instruction state " + status);
162     }
163
164     @Override
165     public synchronized boolean checkedExecutionStart() {
166         if (status != InstructionStatus.Scheduled) {
167             return false;
168         }
169
170         setStatus(InstructionStatus.Executing, null);
171         return true;
172     }
173
174     @Override
175     public synchronized boolean executionHeldUp(final Details details) {
176         if (status != InstructionStatus.Scheduled) {
177             return false;
178         }
179
180         this.heldUpDetails = details;
181         return true;
182     }
183
184     @Override
185     public synchronized void executionCompleted(final InstructionStatus status, final Details details) {
186         Preconditions.checkState(executionFuture != null);
187
188         cancelTimeout();
189
190         // We reuse the preconditions set down in this class
191         final ExecutionResult<Details> result = new ExecutionResult<Details>(status, details);
192         setStatus(status, details);
193         executionFuture.set(result);
194     }
195
196     synchronized void addDependant(final InstructionImpl d) {
197         dependants.add(d);
198     }
199
200     private synchronized void removeDependant(final InstructionImpl d) {
201         dependants.remove(d);
202     }
203
204     private synchronized void removeDependency(final InstructionImpl other) {
205         dependencies.remove(other);
206     }
207
208     synchronized Iterator<InstructionImpl> getDependants() {
209         return dependants.iterator();
210     }
211
212     synchronized void clean() {
213         for (final Iterator<InstructionImpl> it = dependencies.iterator(); it.hasNext();) {
214             it.next().removeDependant(this);
215         }
216         dependencies.clear();
217
218         for (final Iterator<InstructionImpl> it = dependants.iterator(); it.hasNext();) {
219             it.next().removeDependency(this);
220         }
221         dependants.clear();
222
223         this.queue.instructionRemoved();
224     }
225
226     synchronized ListenableFuture<ExecutionResult<Details>> ready() {
227         Preconditions.checkState(status == InstructionStatus.Queued);
228         Preconditions.checkState(executionFuture == null);
229
230         /*
231          * Check all vertices we depend on. We start off as ready for
232          * scheduling. If we encounter a cancelled/failed/unknown
233          * dependency, we cancel this instruction (and cascade). If we
234          * encounter an executing/queued/scheduled dependency, we hold
235          * of scheduling this one.
236          */
237         boolean ready = true;
238
239         final List<InstructionId> unmet = new ArrayList<>();
240         for (final InstructionImpl d : dependencies) {
241             switch (d.getStatus()) {
242             case Cancelled:
243             case Failed:
244             case Unknown:
245                 unmet.add(d.getId());
246                 break;
247             case Executing:
248             case Queued:
249             case Scheduled:
250                 ready = false;
251                 break;
252             case Successful:
253                 // No-op
254                 break;
255             }
256         }
257
258         if (!unmet.isEmpty()) {
259             LOG.warn("Instruction {} was Queued, while some dependencies were resolved unsuccessfully, cancelling it", id);
260             cancel(new DetailsBuilder().setUnmetDependencies(unmet).build());
261             return null;
262         }
263
264         if (!ready) {
265             return null;
266         }
267
268         LOG.debug("Instruction {} is ready for execution", id);
269         setStatus(InstructionStatus.Scheduled, null);
270         executionFuture = SettableFuture.create();
271         schedulingFuture.set(this);
272         return executionFuture;
273     }
274 }