Skip to content

Commit

Permalink
Implement a schedule generator for a QS scheduler.
Browse files Browse the repository at this point in the history
  • Loading branch information
lsk567 committed Sep 7, 2022
1 parent f5f7095 commit f19ec04
Show file tree
Hide file tree
Showing 11 changed files with 803 additions and 23 deletions.
4 changes: 3 additions & 1 deletion org.lflang/src/org/lflang/TargetProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,7 @@ public String getcMakeName() {
/**
* Supported schedulers.
* @author{Soroush Bateni <[email protected]>}
* @author{Shaokai Lin <[email protected]>}
*/
public enum SchedulerOption {
NP(false), // Non-preemptive
Expand All @@ -1373,8 +1374,9 @@ public enum SchedulerOption {
Path.of("data_collection.h")
)),
GEDF_NP(true), // Global EDF non-preemptive
GEDF_NP_CI(true); // Global EDF non-preemptive with chain ID
GEDF_NP_CI(true), // Global EDF non-preemptive with chain ID
// PEDF_NP(true); // Partitioned EDF non-preemptive (FIXME: To be re-added in a future PR)
QS(false); // Quasi-static

/**
* Indicate whether or not the scheduler prioritizes reactions by deadline.
Expand Down
12 changes: 12 additions & 0 deletions org.lflang/src/org/lflang/generator/ReactionInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ public ReactionInstance(
* If left at the default, parallel execution will be based purely
* on levels.
*/
//TODO:
public long reactionid ;
public long chainID = 1L;

/**
Expand Down Expand Up @@ -535,10 +537,20 @@ public class Runtime {
/** ID ranging from 0 to parent.getTotalWidth() - 1. */
public int id = 0;
public int level = 0;
public int reactionID = 0; // Reaction Runtime ID

public ReactionInstance getReaction() {
return ReactionInstance.this;
}

public long getReactionID() {
return this.reactionID;
}

public String getFullName() {
return ReactionInstance.this.toString();
}

@Override
public String toString() {
String result = ReactionInstance.this + "(level: " + level;
Expand Down
49 changes: 35 additions & 14 deletions org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
import java.util.List;
import java.util.Set;

import org.eclipse.xtext.xbase.lib.Exceptions;

import org.lflang.generator.ReactionInstance.Runtime;
import org.lflang.graph.PrecedenceGraph;
import org.lflang.lf.Variable;

/**
* This class analyzes the dependencies between reaction runtime instances.
* For each ReactionInstance, there may be more than one runtime instance because
Expand All @@ -60,18 +61,23 @@ public class ReactionInstanceGraph extends PrecedenceGraph<ReactionInstance.Runt
* Create a new graph by traversing the maps in the named instances
* embedded in the hierarchy of the program.
*/
public ReactionInstanceGraph(ReactorInstance main) {
public ReactionInstanceGraph(ReactorInstance main, boolean detectCycles) {
this.main = main;
this.detectCycles = detectCycles;
rebuild();
}

///////////////////////////////////////////////////////////
//// Public fields

/**
* The main reactor instance that this graph is associated with.
*/
/** The main reactor instance that this graph is associated with. */
public final ReactorInstance main;

/**
* Whether this ReactionInstanceGraph is built for detecting cycles,
* which involves removing nodes from the graph.
*/
public final boolean detectCycles;

///////////////////////////////////////////////////////////
//// Public methods
Expand All @@ -83,14 +89,16 @@ public ReactionInstanceGraph(ReactorInstance main) {
public void rebuild() {
this.clear();
addNodesAndEdges(main);
// Assign a level to each reaction.
// If there are cycles present in the graph, it will be detected here.
assignLevels();
if (nodeCount() != 0) {
// The graph has cycles.
// main.reporter.reportError("Reactions form a cycle! " + toString());
// Do not throw an exception so that cycle visualization can proceed.
// throw new InvalidSourceException("Reactions form a cycle!");
if (this.detectCycles) {
// Assign a level to each reaction.
// If there are cycles present in the graph, it will be detected here.
assignLevels();
if (nodeCount() != 0) {
// The graph has cycles.
// main.reporter.reportError("Reactions form a cycle! " + toString());
// Do not throw an exception so that cycle visualization can proceed.
// throw new InvalidSourceException("Reactions form a cycle!");
}
}
}

Expand All @@ -115,6 +123,19 @@ public int getBreadth() {
return maxBreadth;
}

/*
* Get a reaction in the dependency graph based on a reaction ID.
*/
public Runtime getReactionByID(long reactionID) {
for (var node : this.nodes()) {
if (node.getReactionID() == reactionID)
return node;
}
Exceptions.sneakyThrow(
new Exception("Reaction with ID " + reactionID + " not found."));
return null;
}

///////////////////////////////////////////////////////////
//// Protected methods

Expand Down Expand Up @@ -291,7 +312,7 @@ private void assignLevels() {
}
}

// Remove visited origin.
// (IMPORTANT) Remove visited origin.
removeNode(origin);

// Update numReactionsPerLevel info
Expand Down
3 changes: 2 additions & 1 deletion org.lflang/src/org/lflang/generator/ReactorInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re
public ReactionInstanceGraph assignLevels() {
if (depth != 0) return root().assignLevels();
if (cachedReactionLoopGraph == null) {
cachedReactionLoopGraph = new ReactionInstanceGraph(this);
// The 2nd param, `true`, enables cycle detection.
cachedReactionLoopGraph = new ReactionInstanceGraph(this, true);
}
return cachedReactionLoopGraph;
}
Expand Down
4 changes: 2 additions & 2 deletions org.lflang/src/org/lflang/generator/c/CCmakeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.lflang.FileConfig;
import org.lflang.TargetConfig;
import org.lflang.TargetProperty.Platform;
import org.lflang.TargetProperty.SchedulerOption;
import org.lflang.generator.CodeBuilder;
import org.lflang.util.FileUtil;

Expand Down Expand Up @@ -231,8 +232,7 @@ CodeBuilder generateCMakeCode(
cMakeCode.pr("add_link_options( "+compilerFlag+")");
}
}
cMakeCode.newLine();


// Add the install option
cMakeCode.pr("install(");
cMakeCode.indent();
Expand Down
1 change: 1 addition & 0 deletions org.lflang/src/org/lflang/generator/c/CCoreFilesUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.lflang.generator.c;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down
42 changes: 42 additions & 0 deletions org.lflang/src/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
import org.lflang.TargetProperty.ClockSyncMode;
import org.lflang.TargetProperty.CoordinationType;
import org.lflang.TargetProperty.Platform;
import org.lflang.TargetProperty.SchedulerOption;
import org.lflang.TimeValue;
import org.lflang.federated.FedFileConfig;
import org.lflang.federated.FederateInstance;
Expand All @@ -87,6 +88,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
import org.lflang.generator.TargetTypes;
import org.lflang.generator.TimerInstance;
import org.lflang.generator.TriggerInstance;
import org.lflang.generator.uclid.UclidScheduleGenerator;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Expression;
Expand Down Expand Up @@ -579,6 +581,11 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
Exceptions.sneakyThrow(e);
}

// Generate scheduler-specific code. In the case of the QS scheduler,
// a schedule.h is generated. This is separated from the logic in
// pickScheduler(), since pickScheduler() does not generate new code.
generateCodeForScheduler();

// Create docker file.
if (targetConfig.dockerOptions != null && mainDef != null) {
dockerGenerator.addFile(
Expand Down Expand Up @@ -882,6 +889,25 @@ private void generateCodeForCurrentFederate(
}
}

/**
* Generate scheduler-specific code. In the case of the QS scheduler,
* a schedule.h is generated. This is separated from the logic in
* pickScheduler(), since pickScheduler() does not generate new code.
*/
private void generateCodeForScheduler() {
// Generate schedule.h if QS scheduler is used.
if (targetConfig.schedulerType == SchedulerOption.QS) {
var scheduleGenerator = new UclidScheduleGenerator(fileConfig, errorReporter, main, targetConfig);
var scheduleFile = fileConfig.getSrcGenPath() + File.separator + "schedule.h";
var scheduleCode = scheduleGenerator.generateScheduleCode();
try {
scheduleCode.writeToFile(scheduleFile);
} catch (IOException e) {
Exceptions.sneakyThrow(e);
}
}
}

protected CDockerGenerator getDockerGenerator() {
return new CDockerGenerator(isFederated, CCppMode, targetConfig);
}
Expand Down Expand Up @@ -924,6 +950,22 @@ private void pickScheduler() {
targetConfig.compileAdditionalSources.add(
"core" + File.separator + "utils" + File.separator + "semaphore.c"
);

// Perform a set of QS scheduler-specific operations.
if (targetConfig.schedulerType == SchedulerOption.QS) {
// Define a macro in CMake.
targetConfig.compileDefinitions.put("SCHEDULER_QS", "");
// Copy an additional header file for QS.
try {
FileUtil.copyFilesFromClassPath(
"/lib/c/reactor-c/core",
fileConfig.getSrcGenPath().resolve("core"),
List.of("threaded/scheduler_QS.h")
);
} catch (IOException e) {
Exceptions.sneakyThrow(e);
}
}
}

private boolean hasDeadlines(List<Reactor> reactors) {
Expand Down
Loading

0 comments on commit f19ec04

Please sign in to comment.