blob: bf103a8835d360f6aa23c2c52b4321c4fa030b56 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2019, 2020 Dortmund University of Applied Sciences and Arts and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Dortmund University of Applied Sciences and Arts - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.multicore.execution.logic.systemproxy.multicoresystem;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.ISystemProxy;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.SimException;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.ISchedulerAlgorithm;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.ISchedulerEventListener;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.ISchedulerTask;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.IStepScheduler;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.core.Barrier;
import org.eclipse.app4mc.multicore.execution.logic.systemproxy.scheduler.core.StepScheduler;
public class MultiCoreSystem implements ISystemProxy {
private boolean run = false;
/** Factory to create instances of scheduler algorithms */
private final Supplier<ISchedulerAlgorithm> schedulerAlgoFactory;
/** One scheduler for each core */
private final Map<String, IStepScheduler> schedulers = new HashMap<>();
private final long timeScale;
/**
* Indicates if synchronization through multiple cores is setup. Depending
* on this {@link #compute(long time)}uses different scheduling computation.
*/
private boolean interCoreSynch = false;
boolean interrupted = false;
public MultiCoreSystem(final long timeScale, final Supplier<ISchedulerAlgorithm> schedulerFactory) {
this.timeScale = timeScale;
this.schedulerAlgoFactory = schedulerFactory;
}
/**
* Compute the simulation. This is only permitted once for every instance.
* Time is passed in pico-seconds. Time is scaled by <code>timeScale</code>
* (rounding up) provided at creation.
*
* @param time
* Time to scedule until
* @throws SimException
* If already run.
*/
@Override
public void compute(long time) throws SimException {
if (!this.run) {
this.run = true;
time = SimUtil.scaleRoundUp(time, this.timeScale);
getSchedulers().values().forEach(IStepScheduler::init);
if (this.interCoreSynch) {
simulateSynchronous(time);
}
else {
simulateAsynchronous(time);
}
}
else {
throw new SimException("Cant run sim more than once!");
}
}
@Override
public void interruptComputation() {
this.interrupted = true;
}
/**
* Compute the scheduling for each core-scheduler. As long there is no
* inter-core-synchronization run absolute independently. Therefore this
* mehtod parallelize the computation with up to 10 threads.
*
* @param simulationTime
* @throws SimException
*/
private void simulateAsynchronous(final long simulationTime) throws SimException {
final int num = getSchedulers().size();
final List<Callable<Boolean>> cl = new LinkedList<>();
for (final Map.Entry<String, IStepScheduler> entry : getSchedulers().entrySet()) {
cl.add(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
long timecount = 0;
while (timecount <= simulationTime) {
entry.getValue().runTaskOrIdleStep();
entry.getValue().updateTaskSynchronisation();
entry.getValue().updateTaskSet();
if (MultiCoreSystem.this.interrupted) {
return false;
}
timecount = Math.addExact(timecount, 1);
}
return true;
}
});
}
ExecutorService taskExecutor = Executors.newFixedThreadPool(num > 10 ? 10 : num);
try {
final List<Future<Boolean>> results = taskExecutor.invokeAll(cl);
// invokeAll: Blocks until all callables have finished execution or
// throw exception
taskExecutor = null;
for (final Future<Boolean> result : results) {
if (!Boolean.TRUE.equals(result.get())) {
throw new SimException("Interrupted scheduling!");
}
}
}
catch (final InterruptedException e) {
e.printStackTrace();
// Restore interrupted state...
Thread.currentThread().interrupt();
throw new SimException(e.getMessage());
}
catch (final ExecutionException e) {
e.printStackTrace();
// an error occurred while execution (e.g. scheduler Exception from
// scheduleUntil)
throw new SimException(e.getMessage());
}
}
/**
* This method provides step by step computation of the scheduling. This is
* necessary if there is inter-core-synchronization.
*
* @param simulationTime
* @throws SimException
*/
private void simulateSynchronous(final long simulationTime) throws SimException {
long timecount = 0;
while (timecount <= simulationTime) {
getSchedulers().values().forEach(IStepScheduler::runTaskOrIdleStep);
getSchedulers().values().forEach(IStepScheduler::updateTaskSynchronisation);
getSchedulers().values().forEach(IStepScheduler::updateTaskSet);
if (this.interrupted) {
throw new SimException("Interrupted scheduling!");
}
try {
timecount = Math.addExact(timecount, 1);
}
catch (final ArithmeticException e) {
throw new SimException(e.getMessage());
}
}
}
protected Map<String, IStepScheduler> getSchedulers() {
return this.schedulers;
}
/**
* Add a core to the simulation. Every core has a scheduler which will be
* created from the scheduler factory passed at creation.
*
* @param corename
*/
@Override
public void addCoreScheduler(final String corename) {
if (!getSchedulers().containsKey(corename)) {
final StepScheduler s = new StepScheduler(this.schedulerAlgoFactory.get());
getSchedulers().put(corename, s);
}
}
/**
* Add a SchedulerEventListener to a core. Each core has a independent
* scheduler.
*
* @param coreName
* Name of the core.
* @param l
* Listener-instance.
*/
@Override
public void addListener(final String coreName, final ISchedulerEventListener l) {
getSchedulers().get(coreName).addSchedulerEventListener(l);
}
/**
* Add task to simulation. If provided core does not exist it will be
* created.
*
* @param core
* @param name
* @param wcet
* @param period
*/
@Override
public void addTask(final String core, final String name, long wcet, long period) {
IStepScheduler s = getSchedulers().get(core);
if (s == null) {
addCoreScheduler(core);
s = getSchedulers().get(core);
}
wcet = SimUtil.scaleRoundUp(wcet, this.timeScale);
period = SimUtil.scaleRoundUp(period, this.timeScale);
s.addTask(name, wcet, period);
}
@Override
public void addTaskPrecedence(final String preCore, final String preTask, final String postCore,
final String postTask) throws SimException {
addTaskPrecedence(preCore, preTask, ISchedulerTask.BARRIER_UNLOCK_AT_SUSPENSION, postCore, postTask);
}
@Override
public void addTaskPrecedence(final String preCore, final String preTask, long releaseTime, final String postCore,
final String postTask) throws SimException {
if (preTask.equals(postTask)) {
throw new SimException("Precedence on Task " + preTask + ": Self-Transition is not possible!");
}
final IStepScheduler preCoreScheduler = getSchedulers().get(preCore);
final IStepScheduler postCoreScheduler = getSchedulers().get(postCore);
if (preCoreScheduler == null) {
throw new SimException("No scheduler available for core: " + preCore);
}
else if (postCoreScheduler == null) {
throw new SimException("No scheduler available for core: " + postCore);
}
final ISchedulerTask pre = preCoreScheduler.getTask(preTask);
final ISchedulerTask post = postCoreScheduler.getTask(postTask);
if (pre == null) {
throw new SimException("Task " + preTask + " not available!");
}
else if (post == null) {
throw new SimException("Task " + postTask + " not available!");
}
if (pre.getPeriod() != post.getPeriod()) {
throw new SimException("Task " + preTask + " and " + postTask
+ " do not have an equivalent period! Only equal periods are permitted for task precedence!");
}
final Barrier m = new Barrier();
m.setName("bar_task_precedence_" + preTask + "->" + postTask);
if (releaseTime != ISchedulerTask.BARRIER_UNLOCK_AT_SUSPENSION) {
releaseTime = SimUtil.scaleRoundUp(releaseTime, this.timeScale);
}
pre.addOwnedBarrier(m, releaseTime);
post.addDependentBarrier(m);
if (!preCore.equals(postCore)) {
this.interCoreSynch = true;
}
}
}