Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for broken deadline propagation mechanism #1451

Merged
merged 26 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dd9eda4
Set the deadline member of ReactionInstance when declaredDeadline is set
Nov 2, 2022
7444959
Add getInheritedDeadline
Nov 2, 2022
f919955
Add tests for checking priority of deadline reactions
erlingrj Nov 2, 2022
56dbd5b
Use inherited deadline to assign priority to reaction
Nov 3, 2022
ea95ede
Fix deadline tests. They need to be single-threaded.
erlingrj Nov 3, 2022
397b62e
We need to make string of the UNSIGNED value of the index/deadline
erlingrj Nov 3, 2022
5f61ddf
Format Deadline tests
erlingrj Nov 3, 2022
af4d53b
Rename to inferredDeadline and break cycles when setting it.
erlingrj Nov 5, 2022
3a68ba3
FIx reference to reactionInstance.deadline, should be declaredDeadlin…
erlingrj Nov 5, 2022
36c7b58
Move inferred deadline out of reactionInstance and into Runtime
erlingrj Nov 6, 2022
e05ef70
Implement assignInferredDeadlines in ReactionInstanceGraph. Call it i…
erlingrj Nov 6, 2022
fad06e9
WIP: Use Runtime.deadline in code-generation
erlingrj Nov 6, 2022
0d089e6
Add functions for getting Set and List of deadlines from ReactionInst…
erlingrj Nov 6, 2022
5733906
Fix mistake in assignInferredDeadlines
erlingrj Nov 6, 2022
71b0591
Fix code generation. Get deadlines in same way as levels are got.
erlingrj Nov 6, 2022
e6378e3
Add new test program for Banked reactors
erlingrj Nov 6, 2022
ca43fe1
Increase deadline to 100sec to fix macOS CI
erlingrj Nov 7, 2022
e7ad63c
Detect if there are deadlines globally.
Nov 29, 2022
794de2c
Fix typo
erlingrj Nov 29, 2022
02301f1
Fix some comments
erlingrj Nov 29, 2022
53fd90e
Address Edwards comments
erlingrj Nov 30, 2022
3c95857
Apply suggestions from code review
erlingrj Nov 30, 2022
26b861b
Address Martens comments
erlingrj Nov 30, 2022
1c95d0e
Remove redundant(?) calls to assignLevels
erlingrj Dec 7, 2022
a9c7a7e
Undo mistake
erlingrj Dec 7, 2022
7228484
Merge branch 'master' into fix-index-deadline3
erlingrj Dec 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions org.lflang/src/org/lflang/generator/GeneratorBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ public abstract class GeneratorBase extends AbstractLFValidator {
*/
public boolean hasModalReactors = false;

/**
* Indicates whether the program has any deadlines and thus
* needs to propagate deadlines through the reaction instance graph
*/
public boolean hasDeadlines = false;

// //////////////////////////////////////////
// // Target properties, if they are included.
/**
Expand Down
1 change: 1 addition & 0 deletions org.lflang/src/org/lflang/generator/GeneratorUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.lflang.lf.KeyValuePair;
import org.lflang.lf.KeyValuePairs;
import org.lflang.lf.Model;
import org.lflang.lf.Reaction;
import org.lflang.lf.Reactor;
import org.lflang.lf.TargetDecl;
import org.lflang.util.FileUtil;
Expand Down
1 change: 1 addition & 0 deletions org.lflang/src/org/lflang/generator/ReactionInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ public Set<ReactionInstance> dependsOnReactions() {
public Set<Integer> getLevels() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless assignLevels is invoked in the constructor, there is no assurance that this method will always return a sensible result. Where is assignLevels called?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assignLevels is invoked once in createMainReactorInstance() in CGenerator.c. So it is called once on the top level reactor, which, if my understanding is correct should do the calculation for the whole reaction graph exactly once

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the problem that was addressed by calling assignLevels, "just in case" is that the level of a reaction is not known until you have constructed the whole reaction graph. So you cannot just do it in the constructor. A simple fix would be to add a simple function to reactorInstance which checks if levels has been assigned yet. (hasLevelsBeenAssigned()) this would walk up the containment hierarchy to the main reactor and check if it has created a reactionInstanceGraph yet (and thus assigned levels). If this for some reason is not true, then I propose to throw an error because the developer might be requesting the levels at a point in time where they are not available (e.g. before the reaction graph has been fully constructed).

How does this sound?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that sounds like a good stop-gap measure to me. Still not fond of the original design, but the error will help prevent confusion.

Set<Integer> result = new LinkedHashSet<>();
// Force calculation of levels if it has not been done.
// FIXME: Is it necessary to repeat this everywhere?
erlingrj marked this conversation as resolved.
Show resolved Hide resolved
parent.assignLevels();
for (Runtime runtime : runtimeInstances) {
result.add(runtime.level);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,13 @@ public void rebuild() {
// Do not throw an exception so that cycle visualization can proceed.
// throw new InvalidSourceException("Reactions form a cycle!");
}
// FIXME: Is it OK to also assign deadlines here?
// Also do the traversal in the opposite order to calculate the deadlines
}

public void rebuildAndAssignDeadlines() {
this.clear();
addNodesAndEdges(main);
// Assign a level to each reaction.
// If there are cycles present in the graph, it will be detected here.
assignInferredDeadlines();

this.clear();
}

/*
Expand Down
17 changes: 17 additions & 0 deletions org.lflang/src/org/lflang/generator/ReactorInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,23 @@ public ReactionInstanceGraph assignLevels() {
}
return cachedReactionLoopGraph;
}

/**
* This function assigns/propagates deadlines through the Reaction Instance Graph
* it performs Kahn`s algorithm in reverse, starting from the leaf nodes and
* propagates deadlines upstream. To reduce cost, it should only be invoked when
erlingrj marked this conversation as resolved.
Show resolved Hide resolved
* there are user-specified deadlines in the program
* @return
*/
public ReactionInstanceGraph assignDeadlines() {
if (depth != 0) return root().assignDeadlines();
if (cachedReactionLoopGraph == null) {
cachedReactionLoopGraph = new ReactionInstanceGraph(this);
}
cachedReactionLoopGraph.rebuildAndAssignDeadlines();
return cachedReactionLoopGraph;

erlingrj marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Return the instance of a child rector created by the specified
Expand Down
3 changes: 3 additions & 0 deletions org.lflang/src/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2571,6 +2571,9 @@ private void createMainReactorInstance() {
errorReporter.reportError("Main reactor has causality cycles. Skipping code generation.");
return;
}
if (hasDeadlines) {
this.main.assignDeadlines();
}
erlingrj marked this conversation as resolved.
Show resolved Hide resolved
// Inform the run-time of the breadth/parallelism of the reaction graph
var breadth = reactionInstanceGraph.getBreadth();
if (breadth == 0) {
Expand Down
64 changes: 46 additions & 18 deletions org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -343,30 +343,42 @@ private static boolean setReactionPriorities(
) {
var foundOne = false;
// Force calculation of levels if it has not been done.
// FIXME: Why is this repeated all over? Are there scenarios where we haven't assigned levels yet?
erlingrj marked this conversation as resolved.
Show resolved Hide resolved
reactor.assignLevels();

// If any reaction has multiple levels, because it is in a bank of reactors
// then we need to create an array with the levels and deadlines here
// We handle four different scenarios
// 1) A reactionInstance has 1 level and 1 deadline
// 2) A reactionInstance has 1 level but multiple deadlines
// 3) A reaction instance has multiple levels but all have the same deadline
// 4) Multple deadlines and levels
erlingrj marked this conversation as resolved.
Show resolved Hide resolved

var prolog = new CodeBuilder();
var epilog = new CodeBuilder();
for (ReactionInstance r : reactor.reactions) {
if (currentFederate.contains(r.getDefinition())) {
var levelSet = r.getLevels();
var deadlineSet = r.getInferredDeadlines();

if (levelSet.size() != 1 || deadlineSet.size() != 1) {
if (levelSet.size() > 1 || deadlineSet.size() > 1) {
// Scenario 2-4
if (prolog.length() == 0) {
prolog.startScopedBlock();
epilog.endScopedBlock();
}
// Get inferredDeadlines and map to string representations
}
if (deadlineSet.size() > 1) {
// Scenario (2) or (4)
var deadlines = r.getInferredDeadlinesList().stream()
.map(elt -> ("0x" + Long.toString(elt.toNanoSeconds(), 16) + "LL"))
.collect(Collectors.toList());

prolog.pr("interval_t "+r.uniqueID()+"_inferred_deadlines[] = { "+joinObjects(deadlines, ", ")+" };");
}

if (levelSet.size() > 1) {
// Scenario (3) or (4)
// Cannot use the above set of levels because it is a set, not a list.
prolog.pr("int "+r.uniqueID()+"_levels[] = { "+joinObjects(r.getLevelsList(), ", ")+" };");
prolog.pr("interval_t "+r.uniqueID()+"_inferred_deadlines[] = { "+joinObjects(deadlines, ", ")+" };");
}
}
}
Expand All @@ -382,16 +394,15 @@ private static boolean setReactionPriorities(
// reaction have the same level and deadline
var levelSet = r.getLevels();
var deadlineSet = r.getInferredDeadlines();
if (levelSet.size() == 1 && deadlineSet.size() == 1) {
var level = -1;
for (Integer l : levelSet) {
level = l;
}

var inferredDeadline = TimeValue.MAX_VALUE;
for (TimeValue t : deadlineSet) {
inferredDeadline = t;
}
// Get the head of the associated lists. To avoid duplication in
// several of the following cases
var level = r.getLevelsList().get(0);
var inferredDeadline = r.getInferredDeadlinesList().get(0);
var runtimeIdx =CUtil.runtimeIndex(r.getParent());

if (levelSet.size() == 1 && deadlineSet.size() == 1) {
// Scenario (1)

// xtend doesn't support bitwise operators...
var indexValue = inferredDeadline.toNanoSeconds() << 16 | level;
Expand All @@ -404,11 +415,28 @@ private static boolean setReactionPriorities(
"// deadline "+ inferredDeadline.toNanoSeconds()+" shifted left 16 bits.",
CUtil.reactionRef(r)+".index = "+reactionIndex+";"
));
} else {
// The general case where the different reactions in the bank
// have different level or deadline and thus need its own index
var runtimeIdx =CUtil.runtimeIndex(r.getParent());
} else if (levelSet.size() == 1 && deadlineSet.size() > 1) {
// Scenario 2
temp.pr(String.join("\n",
CUtil.reactionRef(r)+".chain_id = "+r.chainID+";",
"// index is the OR of levels["+runtimeIdx+"] and ",
"// deadlines["+runtimeIdx+"] shifted left 16 bits.",
CUtil.reactionRef(r)+".index = ("+r.uniqueID()+"_inferred_deadlines["+runtimeIdx+"] << 16) | " +
level+";"
));

} else if (levelSet.size() > 1 && deadlineSet.size() == 1) {
// Scenarion (3)
temp.pr(String.join("\n",
CUtil.reactionRef(r)+".chain_id = "+r.chainID+";",
"// index is the OR of levels["+runtimeIdx+"] and ",
"// deadlines["+runtimeIdx+"] shifted left 16 bits.",
CUtil.reactionRef(r)+".index = ("+inferredDeadline.toNanoSeconds()+" << 16) | " +
r.uniqueID()+"_levels["+runtimeIdx+"];"
));

} else if (levelSet.size() > 1 && deadlineSet.size() > 1) {
// Scenario (4)
temp.pr(String.join("\n",
CUtil.reactionRef(r)+".chain_id = "+r.chainID+";",
"// index is the OR of levels["+runtimeIdx+"] and ",
Expand Down