2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.bgpcep.programming.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.SettableFuture;
16 import io.netty.util.Timeout;
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.List;
20 import javax.annotation.concurrent.GuardedBy;
21 import org.opendaylight.bgpcep.programming.spi.ExecutionResult;
22 import org.opendaylight.bgpcep.programming.spi.Instruction;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.CancelFailure;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.InstructionStatus;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.UncancellableInstruction;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.Details;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.programming.rev150720.instruction.status.changed.DetailsBuilder;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
32 final class InstructionImpl implements Instruction {
33 private static final Logger LOG = LoggerFactory.getLogger(InstructionImpl.class);
34 private final List<InstructionImpl> dependants = new ArrayList<>();
35 private final SettableFuture<Instruction> schedulingFuture;
36 private final List<InstructionImpl> dependencies;
37 private final QueueInstruction queue;
38 private final InstructionId id;
39 private SettableFuture<ExecutionResult<Details>> executionFuture;
40 private InstructionStatus status = InstructionStatus.Queued;
41 private Details heldUpDetails;
42 private Timeout timeout;
44 InstructionImpl(final QueueInstruction queue, final SettableFuture<Instruction> future, final InstructionId id,
45 final List<InstructionImpl> dependencies, final Timeout timeout) {
46 this.schedulingFuture = requireNonNull(future);
47 this.dependencies = requireNonNull(dependencies);
48 this.timeout = requireNonNull(timeout);
49 this.queue = requireNonNull(queue);
50 this.id = requireNonNull(id);
53 InstructionId getId() {
57 synchronized InstructionStatus getStatus() {
61 private synchronized void setStatus(final InstructionStatus status, final Details details) {
64 LOG.debug("Instruction {} transitioned to status {}", this.id, status);
66 // Send out a notification
67 this.queue.instructionUpdated(status, details);
86 private void cancelTimeout() {
87 if (this.timeout != null) {
88 this.timeout.cancel();
93 public synchronized void timeout() {
94 if (this.timeout == null) {
98 switch (this.status) {
102 LOG.debug("Instruction {} has status {}, timeout is a no-op", this.id, this.status);
105 LOG.warn("Instruction {} has status {} before timeout completed", this.id, this.status);
108 LOG.info("Instruction {} timed out while executing, transitioning into Unknown", this.id);
109 setStatus(InstructionStatus.Unknown, null);
113 LOG.debug("Instruction {} timed out while Queued, cancelling it", this.id);
114 cancelInstrunction();
117 LOG.debug("Instruction {} timed out while Scheduled, cancelling it", this.id);
118 cancel(this.heldUpDetails);
125 private synchronized void cancelInstrunction() {
126 final List<InstructionId> ids = new ArrayList<>();
127 for (final InstructionImpl d : this.dependencies) {
128 if (d.getStatus() != InstructionStatus.Successful) {
132 cancel(new DetailsBuilder().setUnmetDependencies(ids).build());
136 private void cancelDependants() {
137 final Details details = new DetailsBuilder().setUnmetDependencies(ImmutableList.of(this.id)).build();
138 for (final InstructionImpl d : this.dependants) {
139 d.tryCancel(details);
144 private void cancel(final Details details) {
146 this.schedulingFuture.cancel(false);
147 setStatus(InstructionStatus.Cancelled, details);
150 synchronized Class<? extends CancelFailure> tryCancel(final Details details) {
151 switch (this.status) {
157 LOG.debug("Instruction {} can no longer be cancelled due to status {}", this.id, this.status);
158 return UncancellableInstruction.class;
164 throw new IllegalStateException("Unhandled instruction state " + this.status);
169 public synchronized boolean checkedExecutionStart() {
170 if (this.status != InstructionStatus.Scheduled) {
174 setStatus(InstructionStatus.Executing, null);
179 public synchronized boolean executionHeldUp(final Details details) {
180 if (this.status != InstructionStatus.Scheduled) {
184 this.heldUpDetails = details;
189 public void executionCompleted(final InstructionStatus status, final Details details) {
190 final ExecutionResult<Details> result;
192 synchronized (this) {
193 Preconditions.checkState(this.executionFuture != null);
197 // We reuse the preconditions set down in this class
198 result = new ExecutionResult<>(status, details);
199 setStatus(status, details);
202 this.executionFuture.set(result);
205 synchronized void addDependant(final InstructionImpl d) {
206 this.dependants.add(d);
209 private synchronized void removeDependant(final InstructionImpl d) {
210 this.dependants.remove(d);
213 private synchronized void removeDependency(final InstructionImpl other) {
214 this.dependencies.remove(other);
217 synchronized Iterator<InstructionImpl> getDependants() {
218 return this.dependants.iterator();
221 synchronized void clean() {
222 for (final InstructionImpl dependency : this.dependencies) {
223 dependency.removeDependant(this);
225 this.dependencies.clear();
227 for (final InstructionImpl dependant : this.dependants) {
228 dependant.removeDependency(this);
230 this.dependants.clear();
232 this.queue.instructionRemoved();
235 private Boolean checkDependencies() {
236 boolean ready = true;
237 final List<InstructionId> unmet = new ArrayList<>();
238 for (final InstructionImpl d : this.dependencies) {
239 switch (d.getStatus()) {
243 unmet.add(d.getId());
257 if (!unmet.isEmpty()) {
258 LOG.warn("Instruction {} was Queued, while some dependencies were resolved unsuccessfully, cancelling it", this.id);
259 cancel(new DetailsBuilder().setUnmetDependencies(unmet).build());
265 synchronized ListenableFuture<ExecutionResult<Details>> ready() {
266 Preconditions.checkState(this.status == InstructionStatus.Queued);
267 Preconditions.checkState(this.executionFuture == null);
269 * Check all vertices we depend on. We start off as ready for
270 * scheduling. If we encounter a cancelled/failed/unknown
271 * dependency, we cancel this instruction (and cascade). If we
272 * encounter an executing/queued/scheduled dependency, we hold
273 * of scheduling this one.
275 if (!checkDependencies()) {
278 LOG.debug("Instruction {} is ready for execution", this.id);
279 setStatus(InstructionStatus.Scheduled, null);
280 this.executionFuture = SettableFuture.create();
281 this.schedulingFuture.set(this);
282 return this.executionFuture;