Commit 5c8a7b34 authored by Martti Käärik's avatar Martti Käärik
Browse files

Receiver hub batch mode + proper alternative future handling. &35

parent 9cc7e1be
Loading
Loading
Loading
Loading
Loading
+148 −1
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -1171,7 +1172,153 @@ public class JUnitTestGenerator extends Renderer {
			}
			// Multiple blocks
			else if (b instanceof AlternativeBehaviour) {
				// XXX

				String altsListName = "callables_" + getElementName(b);
				line("List<ExecutionCallable> " + altsListName + " = new java.util.LinkedList<>();");

				// Collect info for each alternative block
				List<FutureInfo> altCallables = new LinkedList<>();
				List<Behaviour> altTriggers = new LinkedList<>();
				List<Integer> altTriggerIndices = new LinkedList<>();
				List<Block> altBlocks = new LinkedList<>();

				List<Block> blocks = ((AlternativeBehaviour) b).getBlock();
				for (int blockIdx = 0; blockIdx < blocks.size(); blockIdx++) {
					Block bl = blocks.get(blockIdx);

					// Find first participating tester-input in this block
					Behaviour triggerBehaviour = null;
					int triggerIndex = -1;
					for (int i = 0; i < bl.getBehaviour().size(); i++) {
						Behaviour beh = bl.getBehaviour().get(i);
						if (isParticipating(beh)) {
							if (!isTesterInput(beh))
								throw new RuntimeException(
										"Alternative block should start with tester input: " + getMessage(beh));
							triggerBehaviour = beh;
							triggerIndex = i;
							break;
						}
					}
					if (triggerBehaviour == null)
						continue;

					lineComment("Alternative " + blockIdx);

					// Handle guard
					Optional<LocalExpression> guard = bl.getGuard().stream()
							.filter(le -> isCurrentComponentInstance(le.getComponentInstance())).findFirst();
					if (guard.isPresent()) {
						DataUse g = guard.get().getExpression();
						initializeDataUse(g, dataUseVariables);
						append("if (");
						write(g, dataUseVariables);
						append(") ");
					}

					// Create callable (without submitting)
					FutureInfo callable = writeTesterInput(triggerBehaviour, dataUseVariables, false, false);
					line(altsListName + ".add(" + callable.varName + ");");
					newLine();

					altCallables.add(callable);
					altTriggers.add(triggerBehaviour);
					altTriggerIndices.add(triggerIndex);
					altBlocks.add(bl);
				}

				if (!altCallables.isEmpty()) {
					newLine();

					// Submit all alternatives (with batch mode on ReceiverHubs)
					String futuresName = "altFutures_" + getElementName(b);
					line("List<Future<ExecutionResult>> " + futuresName + " = " + COMPONENT_FIELD
							+ ".executeAlternatives(" + altsListName + ");");

					// Submit exceptionals
					String excName = "altExc_" + getElementName(b);
					line("List<Future<ExecutionResult>> " + excName + " = " + COMPONENT_FIELD
							+ ".executeExceptionals();");
					newLine();

					// Wait for first completion
					String winnerName = "altWinner_" + getElementName(b);
					line("Future<ExecutionResult> " + winnerName + " = " + COMPONENT_FIELD + ".next();");
					newLine();

					append("try ");
					blockOpen();

					// If/else for each alternative
					for (int i = 0; i < altCallables.size(); i++) {
						FutureInfo f = altCallables.get(i);
						Behaviour trigger = altTriggers.get(i);
						int trigIdx = altTriggerIndices.get(i);
						Block bl = altBlocks.get(i);

						if (i > 0)
							append(" else ");

						append("if (" + futuresName + ".get(" + i + ") == " + winnerName + ")");
						blockOpen();

						// Get result
						line(f.kind + " result = (" + f.kind + ") " + futuresName + ".get(" + i + ").get();");

						// Handle InteractionResult (value assignments from receive)
						if (f.kind.equals("InteractionResult") && trigger instanceof Interaction) {
							Optional<Target> localTargetOpt = ((Interaction) trigger).getTarget().stream()
									.filter(t -> isCurrentComponentInstance(t.getTargetGate().getComponent()))
									.findFirst();
							if (localTargetOpt.isPresent() && trigger instanceof Message) {
								for (ValueAssignment va : localTargetOpt.get().getValueAssignment()) {
									lineComment("ValueAssignment");
									Variable var = va.getVariable();
									append(COMPONENT_FIELD + "." + getElementName(var));
									append(" = ");
									append("(" + getElementName(var.getDataType()) + ")");
									line("result.data.getValue();");
								}
							}
						}

						// Write remaining behaviours of this block
						for (int j = trigIdx + 1; j < bl.getBehaviour().size(); j++) {
							write(bl.getBehaviour().get(j), dataUseVariables, null, thrownExceptions);
							newLine();
						}

						blockClose();
					}

					// Exceptional behaviour fallback
					append(" else ");
					blockOpen();
					line("ExceptionalBehaviour exceptionalBehaviour = " + COMPONENT_FIELD
							+ ".getExceptionalBehaviour(" + winnerName + ");");
					append("if (exceptionalBehaviour != null) ");
					blockOpen();
					line("exceptionalBehaviour.behaviour.execute();");
					blockClose();
					blockClose();
					thrownExceptions.add(STOP_EXCEPTION);

					blockClose();
					append("finally ");
					blockOpen();

					// Cleanup: disable batch mode, cancel futures
					line(COMPONENT_FIELD + ".cleanupAlternatives(" + altsListName + ");");
					line(futuresName + ".forEach(f -> " + COMPONENT_FIELD + ".stop(f));");
					line(excName + ".forEach(f -> " + COMPONENT_FIELD + ".stop(f));");

					blockClose();

					thrownExceptions.add(INTERRUPTED_EXCEPTION);
					thrownExceptions.add(FUTURE_EXECUTION_EXCEPTION);

					newLine();
				}

			} else if (b instanceof ConditionalBehaviour) {
				List<Block> blocks = ((ConditionalBehaviour) b).getBlock();
+32 −3
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ public class ReceiverHub {
	private List<Expectable> expecting = Collections.synchronizedList(new ArrayList<>());
	private ExceptionalBehaviour anyReceiver;

	private int batchSize = 0;
	private int batchCount = 0;

	public ReceiverHub(SystemAdapter systemAdapter, Connection connection, NamedElement testerComponent) {
		this.systemAdapter = systemAdapter;
		this.connection = connection;
@@ -40,12 +43,28 @@ public class ReceiverHub {
		this.anyReceiver = anyReceiver;
	}

	public void enableBatch(int size) {
		synchronized (expecting) {
			this.batchSize = size;
			this.batchCount = 0;
		}
	}

	public void disableBatch() {
		synchronized (expecting) {
			this.batchSize = 0;
			this.batchCount = 0;
			expecting.notifyAll();
		}
	}

	private synchronized void run() {
		int currentlyExpectingIndex = 0;
		while (!stopped) {
			Expectable currentlyExpecting = null;
			synchronized (expecting) {
				while (expecting.size() <= currentlyExpectingIndex)
				while (expecting.size() <= currentlyExpectingIndex
						|| (batchSize > 0 && batchCount < batchSize))
					try {
						expecting.wait();
					} catch (InterruptedException e) {
@@ -63,7 +82,8 @@ public class ReceiverHub {
						synchronized (expecting) {
							for (Expectable e: expecting)
								if (!e.ignoreUnmatched) {
									currentlyExpecting = expecting.get(0);
//									currentlyExpecting = expecting.get(0);
									currentlyExpecting = e;
									break;
								}
						}
@@ -93,8 +113,17 @@ public class ReceiverHub {
		Expectable expectable = new Expectable(expected, ignoreUntil);
		synchronized (expecting) {
			expecting.add(expectable);
			if (batchSize > 0) {
				batchCount++;
				if (batchCount >= batchSize) {
					batchSize = 0;
					batchCount = 0;
					expecting.notifyAll();
				}
			} else {
				expecting.notifyAll();
			}
		}
		try {
			synchronized (expectable) {
				try {
+38 −2
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
@@ -187,6 +188,37 @@ public class TestControl {
		}
	}

	public abstract class InteractionCallable extends ExecutionCallableWithDefaults {
		public abstract ReceiverHub getReceiver();
	}

	public List<Future<ExecutionResult>> executeAlternatives(List<ExecutionCallable> callables) {
		// Count InteractionCallables per hub and enable batch mode
		Map<ReceiverHub, Integer> hubCounts = new HashMap<>();
		for (ExecutionCallable c : callables) {
			if (c instanceof InteractionCallable) {
				ReceiverHub hub = ((InteractionCallable) c).getReceiver();
				hubCounts.merge(hub, 1, Integer::sum);
			}
		}
		hubCounts.forEach((hub, count) -> hub.enableBatch(count));

		// Submit all callables
		List<Future<ExecutionResult>> futures = new ArrayList<>();
		for (ExecutionCallable c : callables) {
			futures.add(c.execute());
		}
		return futures;
	}

	public void cleanupAlternatives(List<ExecutionCallable> callables) {
		for (ExecutionCallable c : callables) {
			if (c instanceof InteractionCallable) {
				((InteractionCallable) c).getReceiver().disableBatch();
			}
		}
	}

	public List<Future<ExecutionResult>> executeExceptionals() {
		List<Future<ExecutionResult>> futures = new ArrayList<>();
		exceptionalBehaviours.forEach(exc -> futures.add(exc.execute()));
@@ -257,10 +289,14 @@ public class TestControl {
	}

	public ExecutionCallable receive(Data expected, Connection connection, boolean isTrigger) {
		ExecutionCallableWithDefaults c = new ExecutionCallableWithDefaults() {
		InteractionCallable c = new InteractionCallable() {
			@Override
			public ReceiverHub getReceiver() {
				return TestControl.this.getReceiver(connection);
			}
			@Override
			public ExecutionResult call() throws Exception {
				Data data = getReceiver(connection).receive(expected, isTrigger);
				Data data = getReceiver().receive(expected, isTrigger);
				return data != null ? new InteractionResult(data) : null;
			}
		};