From dd9eda4f19158b8b7d29cdcde58f96724106e760 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 1 Nov 2022 18:52:04 -0700 Subject: [PATCH 01/25] Set the deadline member of ReactionInstance when declaredDeadline is set --- org.lflang/src/org/lflang/generator/ReactionInstance.java | 1 + 1 file changed, 1 insertion(+) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index f20962d70d..ad5700357c 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -186,6 +186,7 @@ public ReactionInstance( if (this.definition.getDeadline() != null) { this.declaredDeadline = new DeadlineInstance( this.definition.getDeadline(), this); + this.deadline = this.declaredDeadline.maxDelay; } } From 7444959f9b7411cbe0e36a5c23f51fd34df2f045 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 1 Nov 2022 20:20:20 -0700 Subject: [PATCH 02/25] Add getInheritedDeadline --- .../org/lflang/generator/ReactionInstance.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index ad5700357c..2e667c1e61 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -500,6 +500,22 @@ public TimeValue assignLogicalExecutionTime() { return this.let = let; } + /** + * Find the inherited deadline which is the least deadline of all + * downstream reactions. This inherited deadline is used to assign + * priority to the reaction + */ + // FIXME: Needs to be recursive to support chains. + public TimeValue getInheritedDeadline() { + var minDeadline = TimeValue.MAX_VALUE; + for (ReactionInstance r : dependentReactions()) { + if (r.deadline.isEarlierThan(minDeadline)) { + minDeadline = r.deadline; + } + } + return minDeadline; + } + ////////////////////////////////////////////////////// //// Private variables. From f919955fdf02d84218cbe68603f62cfb45bbdddd Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Wed, 2 Nov 2022 09:57:45 -0700 Subject: [PATCH 03/25] Add tests for checking priority of deadline reactions --- test/C/src/DeadlineInherited.lf | 32 +++++++++++++++++++++++++ test/C/src/DeadlinePriority.lf | 0 test/C/src/DeadlineWithAfterDelay.lf | 35 ++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 test/C/src/DeadlineInherited.lf create mode 100644 test/C/src/DeadlinePriority.lf create mode 100644 test/C/src/DeadlineWithAfterDelay.lf diff --git a/test/C/src/DeadlineInherited.lf b/test/C/src/DeadlineInherited.lf new file mode 100644 index 0000000000..3477e6d436 --- /dev/null +++ b/test/C/src/DeadlineInherited.lf @@ -0,0 +1,32 @@ +// Test to verify that deadline priority are inherited +target C { + timeout: 1 sec +} + +preamble {= + int global_cnt = 0; +=} + +reactor NoDeadline { + timer t(0 msec, 100 msec) + reaction(t) {= + global_cnt++; + =} +} + +reactor WithDeadline { + timer t(0 msec, 100 msec) + reaction(t) {==} + reaction(t) {= + if (global_cnt != 0) { + lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); + } + global_cnt--; + =} deadline (100 msec) {==} +} + +main reactor { + a = new NoDeadline() + b = new WithDeadline() +} + diff --git a/test/C/src/DeadlinePriority.lf b/test/C/src/DeadlinePriority.lf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/C/src/DeadlineWithAfterDelay.lf b/test/C/src/DeadlineWithAfterDelay.lf new file mode 100644 index 0000000000..b256507c6d --- /dev/null +++ b/test/C/src/DeadlineWithAfterDelay.lf @@ -0,0 +1,35 @@ +// Test to verify that deadline priority are inherited when using after delays +target C { + timeout: 1 sec +} + +preamble {= + int global_cnt = 0; +=} + +reactor Source { + output out:int + timer t(0 msec, 100 msec) + reaction(t) -> out {= + lf_set(out, 1); + global_cnt++; + =} +} + +reactor SinkWithDeadline { + input in:int; + reaction(in) {= + global_cnt--; + if (global_cnt != 0) { + lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); + } + =} deadline (100 msec) {= + =} +} + +main reactor { + a = new Source() + b = new SinkWithDeadline() + a.out -> b.in after 100 msec +} + From 56dbd5bcd821f72b0344f0e63d76be33138210c7 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Thu, 3 Nov 2022 10:18:03 -0700 Subject: [PATCH 04/25] Use inherited deadline to assign priority to reaction --- .../lflang/generator/ReactionInstance.java | 20 +++++++++++++------ .../generator/c/CTriggerObjectsGenerator.java | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index 2e667c1e61..dee3a011ce 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -504,16 +504,24 @@ public TimeValue assignLogicalExecutionTime() { * Find the inherited deadline which is the least deadline of all * downstream reactions. This inherited deadline is used to assign * priority to the reaction + * FIXME: This can be more efficient by actually storing, the inferred deadline + * in each Reaction and reusing it. Instead of "pushing" all the way down + * recursively for each Reaction */ - // FIXME: Needs to be recursive to support chains. - public TimeValue getInheritedDeadline() { - var minDeadline = TimeValue.MAX_VALUE; + public TimeValue getInferredDeadline() { + var currDeadline = TimeValue.MAX_VALUE; + if (declaredDeadline != null) { + currDeadline = declaredDeadline.maxDelay; + } + for (ReactionInstance r : dependentReactions()) { - if (r.deadline.isEarlierThan(minDeadline)) { - minDeadline = r.deadline; + var childDeadline = r.getInferredDeadline(); + if (childDeadline.isEarlierThan(currDeadline)) { + currDeadline = childDeadline; } } - return minDeadline; + + return currDeadline; } ////////////////////////////////////////////////////// diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 44f059d64c..75b89e2e3e 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -368,6 +368,9 @@ private static boolean setReactionPriorities( for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { foundOne = true; + // Update the inferredDeadline of reaction. + r.deadline = r.getInferredDeadline(); + // The most common case is that all runtime instances of the // reaction have the same level, so deal with that case // specially. From ea95edeeadb1fe3a7db90d4dd83bd73f52be0bfa Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Thu, 3 Nov 2022 10:53:21 -0700 Subject: [PATCH 05/25] Fix deadline tests. They need to be single-threaded. --- test/C/src/DeadlineInherited.lf | 3 ++- test/C/src/DeadlinePriority.lf | 34 ++++++++++++++++++++++++++++ test/C/src/DeadlineWithAfterDelay.lf | 4 +++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/test/C/src/DeadlineInherited.lf b/test/C/src/DeadlineInherited.lf index 3477e6d436..4d6c5a149f 100644 --- a/test/C/src/DeadlineInherited.lf +++ b/test/C/src/DeadlineInherited.lf @@ -1,6 +1,7 @@ // Test to verify that deadline priority are inherited target C { - timeout: 1 sec + timeout: 1 sec, + threading: false } preamble {= diff --git a/test/C/src/DeadlinePriority.lf b/test/C/src/DeadlinePriority.lf index e69de29bb2..0c449db5b9 100644 --- a/test/C/src/DeadlinePriority.lf +++ b/test/C/src/DeadlinePriority.lf @@ -0,0 +1,34 @@ +// Test to verify that deadline gives priority +target C { + timeout: 1 sec, + threading:false +} + +preamble {= + int global_cnt = 0; +=} + +reactor NoDeadline { + timer t(0 msec, 100 msec) + reaction(t) {= + global_cnt++; + =} +} + +reactor WithDeadline { + timer t(0 msec, 100 msec) + reaction(t) {= + if (global_cnt != 0) { + lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); + } + global_cnt--; + =} deadline (100 msec) {= + =} +} + +main reactor { + a = new NoDeadline() + b = new WithDeadline() +} + + diff --git a/test/C/src/DeadlineWithAfterDelay.lf b/test/C/src/DeadlineWithAfterDelay.lf index b256507c6d..1a5fa97a1b 100644 --- a/test/C/src/DeadlineWithAfterDelay.lf +++ b/test/C/src/DeadlineWithAfterDelay.lf @@ -1,6 +1,7 @@ // Test to verify that deadline priority are inherited when using after delays target C { - timeout: 1 sec + timeout: 1 sec, + threading: false } preamble {= @@ -31,5 +32,6 @@ main reactor { a = new Source() b = new SinkWithDeadline() a.out -> b.in after 100 msec + // a.out -> b.in } From 397b62e7e1539c626050f145eec9c315229be87b Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Thu, 3 Nov 2022 10:53:53 -0700 Subject: [PATCH 06/25] We need to make string of the UNSIGNED value of the index/deadline --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 75b89e2e3e..57f8207dff 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -382,7 +382,8 @@ private static boolean setReactionPriorities( } // xtend doesn't support bitwise operators... var indexValue = r.deadline.toNanoSeconds() << 16 | level; - var reactionIndex = "0x" + Long.toString(indexValue, 16) + "LL"; + + var reactionIndex = "0x" + Long.toUnsignedString(indexValue, 16) + "LL"; temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", From 5f61ddf10cbb8e0194e938dad7dac27be11fde60 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Thu, 3 Nov 2022 10:54:29 -0700 Subject: [PATCH 07/25] Format Deadline tests --- test/C/src/DeadlineInherited.lf | 16 +++++++--------- test/C/src/DeadlinePriority.lf | 17 ++++++----------- test/C/src/DeadlineWithAfterDelay.lf | 17 +++++++---------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/test/C/src/DeadlineInherited.lf b/test/C/src/DeadlineInherited.lf index 4d6c5a149f..3cf13e92c1 100644 --- a/test/C/src/DeadlineInherited.lf +++ b/test/C/src/DeadlineInherited.lf @@ -4,30 +4,28 @@ target C { threading: false } -preamble {= - int global_cnt = 0; -=} +preamble {= int global_cnt = 0; =} reactor NoDeadline { timer t(0 msec, 100 msec) - reaction(t) {= - global_cnt++; - =} + + reaction(t) {= global_cnt++; =} } reactor WithDeadline { timer t(0 msec, 100 msec) - reaction(t) {==} + + reaction(t) {= =} + reaction(t) {= if (global_cnt != 0) { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } global_cnt--; - =} deadline (100 msec) {==} + =} deadline(100 msec) {= =} } main reactor { a = new NoDeadline() b = new WithDeadline() } - diff --git a/test/C/src/DeadlinePriority.lf b/test/C/src/DeadlinePriority.lf index 0c449db5b9..09d710b035 100644 --- a/test/C/src/DeadlinePriority.lf +++ b/test/C/src/DeadlinePriority.lf @@ -1,34 +1,29 @@ // Test to verify that deadline gives priority target C { timeout: 1 sec, - threading:false + threading: false } -preamble {= - int global_cnt = 0; -=} +preamble {= int global_cnt = 0; =} reactor NoDeadline { timer t(0 msec, 100 msec) - reaction(t) {= - global_cnt++; - =} + + reaction(t) {= global_cnt++; =} } reactor WithDeadline { timer t(0 msec, 100 msec) + reaction(t) {= if (global_cnt != 0) { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } global_cnt--; - =} deadline (100 msec) {= - =} + =} deadline(100 msec) {= =} } main reactor { a = new NoDeadline() b = new WithDeadline() } - - diff --git a/test/C/src/DeadlineWithAfterDelay.lf b/test/C/src/DeadlineWithAfterDelay.lf index 1a5fa97a1b..c02b22f699 100644 --- a/test/C/src/DeadlineWithAfterDelay.lf +++ b/test/C/src/DeadlineWithAfterDelay.lf @@ -4,13 +4,12 @@ target C { threading: false } -preamble {= - int global_cnt = 0; -=} +preamble {= int global_cnt = 0; =} reactor Source { - output out:int + output out: int timer t(0 msec, 100 msec) + reaction(t) -> out {= lf_set(out, 1); global_cnt++; @@ -18,20 +17,18 @@ reactor Source { } reactor SinkWithDeadline { - input in:int; + input in: int + reaction(in) {= global_cnt--; if (global_cnt != 0) { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } - =} deadline (100 msec) {= - =} + =} deadline(100 msec) {= =} } main reactor { a = new Source() b = new SinkWithDeadline() - a.out -> b.in after 100 msec - // a.out -> b.in + a.out -> b.in after 100 msec // a.out -> b.in } - From af4d53b7fbe84d36c41aa5662d0fff8ca451a466 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sat, 5 Nov 2022 10:19:25 -0700 Subject: [PATCH 08/25] Rename to inferredDeadline and break cycles when setting it. --- .../lflang/generator/ReactionInstance.java | 32 ++++++++++--------- .../generator/c/CTriggerObjectsGenerator.java | 13 ++++---- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index dee3a011ce..1c234deb6e 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -186,7 +186,6 @@ public ReactionInstance( if (this.definition.getDeadline() != null) { this.declaredDeadline = new DeadlineInstance( this.definition.getDeadline(), this); - this.deadline = this.declaredDeadline.maxDelay; } } @@ -225,9 +224,9 @@ public ReactionInstance( public DeadlineInstance declaredDeadline; /** - * Inferred deadline. Defaults to the maximum long value. + * Inferred deadline. */ - public TimeValue deadline = new TimeValue(TimeValue.MAX_LONG_DEADLINE, TimeUnit.NANO); + public TimeValue inferredDeadline = null; /** * Sadly, we have no way to mark reaction "unordered" in the AST, @@ -501,27 +500,30 @@ public TimeValue assignLogicalExecutionTime() { } /** - * Find the inherited deadline which is the least deadline of all - * downstream reactions. This inherited deadline is used to assign - * priority to the reaction - * FIXME: This can be more efficient by actually storing, the inferred deadline - * in each Reaction and reusing it. Instead of "pushing" all the way down - * recursively for each Reaction + * This function sets the inferredDeadline of the reaction. + * The inferred deadline is the minimum of the reactions declaredDeadline + * and the inferredDeadline of any dependent reaction. This is a recursive definition. */ - public TimeValue getInferredDeadline() { + public void setInferredDeadline() { var currDeadline = TimeValue.MAX_VALUE; if (declaredDeadline != null) { currDeadline = declaredDeadline.maxDelay; } for (ReactionInstance r : dependentReactions()) { - var childDeadline = r.getInferredDeadline(); - if (childDeadline.isEarlierThan(currDeadline)) { - currDeadline = childDeadline; + // In a spiralling bank of reactions, a reaction can depend on itself + // detect and break this cycle. + if (r != this) { + if (r.inferredDeadline == null) { + r.setInferredDeadline(); + } + + if (r.inferredDeadline.isEarlierThan(currDeadline)) { + currDeadline = r.inferredDeadline; + } } } - - return currDeadline; + inferredDeadline = currDeadline; } ////////////////////////////////////////////////////// diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 57f8207dff..e8ae6e1167 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -368,8 +368,9 @@ private static boolean setReactionPriorities( for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { foundOne = true; - // Update the inferredDeadline of reaction. - r.deadline = r.getInferredDeadline(); + + // Set the inferredDeadline field in all reactions. + r.setInferredDeadline(); // The most common case is that all runtime instances of the // reaction have the same level, so deal with that case @@ -381,23 +382,23 @@ private static boolean setReactionPriorities( level = l; } // xtend doesn't support bitwise operators... - var indexValue = r.deadline.toNanoSeconds() << 16 | level; + var indexValue = r.inferredDeadline.toNanoSeconds() << 16 | level; var reactionIndex = "0x" + Long.toUnsignedString(indexValue, 16) + "LL"; temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", "// index is the OR of level "+level+" and ", - "// deadline "+r.deadline.toNanoSeconds()+" shifted left 16 bits.", + "// deadline "+r.inferredDeadline.toNanoSeconds()+" shifted left 16 bits.", CUtil.reactionRef(r)+".index = "+reactionIndex+";" )); } else { - var reactionDeadline = "0x" + Long.toString(r.deadline.toNanoSeconds(), 16) + "LL"; + var reactionDeadline = "0x" + Long.toString(r.inferredDeadline.toNanoSeconds(), 16) + "LL"; temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", "// index is the OR of levels["+CUtil.runtimeIndex(r.getParent())+"] and ", - "// deadline "+r.deadline.toNanoSeconds()+" shifted left 16 bits.", + "// deadline "+r.inferredDeadline.toNanoSeconds()+" shifted left 16 bits.", CUtil.reactionRef(r)+".index = ("+reactionDeadline+" << 16) | "+r.uniqueID()+"_levels["+CUtil.runtimeIndex(r.getParent())+"];" )); } From 3a68ba3b9532c20c8832511dc4abc4af075a4abb Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sat, 5 Nov 2022 10:44:17 -0700 Subject: [PATCH 09/25] FIx reference to reactionInstance.deadline, should be declaredDeadline I think --- .../diagram/synthesis/styles/LinguaFrancaShapeExtensions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java index f128cee7b4..326f98f8a6 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/styles/LinguaFrancaShapeExtensions.java @@ -446,7 +446,7 @@ public KPolygon addReactionFigure(KNode node, ReactionInstance reaction) { if (hasDeadlineCode) { KText contentContainerText = _kContainerRenderingExtensions.addText(contentContainer, _utilityExtensions.trimCode(reaction.getDefinition().getDeadline().getCode())); - associateWith(contentContainerText, reaction.deadline); + associateWith(contentContainerText, reaction.declaredDeadline); _kRenderingExtensions.setForeground(contentContainerText, Colors.BROWN); _kRenderingExtensions.setFontSize(contentContainerText, 6); _kRenderingExtensions.setFontName(contentContainerText, KlighdConstants.DEFAULT_MONOSPACE_FONT_NAME); From 36c7b581d6ebb8d7337c6dadd79749e5e9089142 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 01:03:59 -0700 Subject: [PATCH 10/25] Move inferred deadline out of reactionInstance and into Runtime --- .../lflang/generator/ReactionInstance.java | 51 ++++++------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index 1c234deb6e..92f15e362e 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -223,11 +223,6 @@ public ReactionInstance( */ public DeadlineInstance declaredDeadline; - /** - * Inferred deadline. - */ - public TimeValue inferredDeadline = null; - /** * Sadly, we have no way to mark reaction "unordered" in the AST, * so instead, we use a magic comment at the start of the reaction body. @@ -499,33 +494,6 @@ public TimeValue assignLogicalExecutionTime() { return this.let = let; } - /** - * This function sets the inferredDeadline of the reaction. - * The inferred deadline is the minimum of the reactions declaredDeadline - * and the inferredDeadline of any dependent reaction. This is a recursive definition. - */ - public void setInferredDeadline() { - var currDeadline = TimeValue.MAX_VALUE; - if (declaredDeadline != null) { - currDeadline = declaredDeadline.maxDelay; - } - - for (ReactionInstance r : dependentReactions()) { - // In a spiralling bank of reactions, a reaction can depend on itself - // detect and break this cycle. - if (r != this) { - if (r.inferredDeadline == null) { - r.setInferredDeadline(); - } - - if (r.inferredDeadline.isEarlierThan(currDeadline)) { - currDeadline = r.inferredDeadline; - } - } - } - inferredDeadline = currDeadline; - } - ////////////////////////////////////////////////////// //// Private variables. @@ -557,11 +525,11 @@ public void setInferredDeadline() { /** Inner class representing a runtime instance. */ public class Runtime { - public TimeValue deadline = TimeValue.MAX_VALUE; - public Runtime dominating = null; + public TimeValue deadline; + public Runtime dominating; /** ID ranging from 0 to parent.getTotalWidth() - 1. */ - public int id = 0; - public int level = 0; + public int id; + public int level; public ReactionInstance getReaction() { return ReactionInstance.this; @@ -578,5 +546,16 @@ public String toString() { result += ")"; return result; } + + public Runtime() { + this.dominating = null; + this.id = 0; + this.level = 0; + if (ReactionInstance.this.declaredDeadline != null) { + this.deadline = ReactionInstance.this.declaredDeadline.maxDelay; + } else { + this.deadline = TimeValue.MAX_VALUE; + } + } } } From e05ef70688c0b15313f1d99e10170a737e52e59e Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 01:05:43 -0700 Subject: [PATCH 11/25] Implement assignInferredDeadlines in ReactionInstanceGraph. Call it in rebuild --- .../generator/ReactionInstanceGraph.java | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index 72c20bb63a..c5403c89fb 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -92,6 +92,13 @@ public void rebuild() { // Do not throw an exception so that cycle visualization can proceed. // throw new InvalidSourceException("Reactions form a cycle!"); } + // Also do the traversal in the opposite order to calculate the deadlines + this.clear(); + addNodesAndEdges(main); + // Assign a level to each reaction. + // If there are cycles present in the graph, it will be detected here. + assignInferredDeadlines(); + } /* @@ -298,7 +305,48 @@ private void assignLevels() { adjustNumReactionsPerLevel(origin.level, 1); } } - + + /** + * This function assigns inferred deadlines to all the reactions in the graph. + * It is modeled after `assignLevels` but it starts at the leaf nodes and uses + * Kahns algorithm to build a reverse topologically sorted graph + * + */ + private void assignInferredDeadlines() { + List start = new ArrayList<>(leafNodes()); + + // All leaf nodes have deadline initialized to their declared deadline or MAX_VALUE + + while (!start.isEmpty()) { + Runtime origin = start.remove(0); + Set toRemove = new LinkedHashSet<>(); + Set upstreamAdjacentNodes = getUpstreamAdjacentNodes(origin); + + // Visit effect nodes. + for (Runtime upstream : upstreamAdjacentNodes) { + // Stage edge between origin and upstream for removal. + toRemove.add(upstream); + + // Update deadline of upstream node if origins deadline is earlier. + if (origin.deadline.isEarlierThan(upstream.deadline)) { + upstream.deadline = origin.deadline; + } + } + // Remove visited edges. + for (Runtime upstream : toRemove) { + removeEdge(upstream, origin); + // If the upstream node has no more outgoing edges, + // then move it in the start set. + if (getDownstreamAdjacentNodes(upstream).size() == 0) { + start.add(upstream); + } + } + + // Remove visited origin. + removeNode(origin); + } + + } /** * Adjust {@link #numReactionsPerLevel} at index level by From fad06e90c79a3eef1c0141f081e5e0ecfe1e7c2b Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 01:06:47 -0700 Subject: [PATCH 12/25] WIP: Use Runtime.deadline in code-generation --- .../generator/c/CTriggerObjectsGenerator.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index e8ae6e1167..31b7ec351f 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -17,6 +17,7 @@ import org.lflang.ASTUtils; import org.lflang.AttributeUtils; import org.lflang.TargetConfig; +import org.lflang.TimeValue; import org.lflang.TargetProperty.CoordinationType; import org.lflang.TargetProperty.LogLevel; import org.lflang.federated.CGeneratorExtension; @@ -369,8 +370,6 @@ private static boolean setReactionPriorities( if (currentFederate.contains(r.getDefinition())) { foundOne = true; - // Set the inferredDeadline field in all reactions. - r.setInferredDeadline(); // The most common case is that all runtime instances of the // reaction have the same level, so deal with that case @@ -381,19 +380,27 @@ private static boolean setReactionPriorities( for (Integer l : levels) { level = l; } + var inferredDeadline = r.getRuntimeInstances().get(0).deadline; + // xtend doesn't support bitwise operators... - var indexValue = r.inferredDeadline.toNanoSeconds() << 16 | level; + var indexValue = inferredDeadline.toNanoSeconds() << 16 | level; var reactionIndex = "0x" + Long.toUnsignedString(indexValue, 16) + "LL"; temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", "// index is the OR of level "+level+" and ", - "// deadline "+r.inferredDeadline.toNanoSeconds()+" shifted left 16 bits.", + "// deadline "+ inferredDeadline.toNanoSeconds()+" shifted left 16 bits.", CUtil.reactionRef(r)+".index = "+reactionIndex+";" )); } else { - var reactionDeadline = "0x" + Long.toString(r.inferredDeadline.toNanoSeconds(), 16) + "LL"; + // This instance belongs to a bank of Reactors. + // FIXME: We need a way to get the bank_idx of the reactor containing this reaction + // AFAICS its the only way to get the right RunTime and thus the right deadline + var bankIdx=1; + var inferredDeadline = r.getRuntimeInstances().get(bankIdx); + + var reactionDeadline = "0x" + Long.toString(inferredDeadline.toNanoSeconds(), 16) + "LL"; temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", From 0d089e6a8a893e2723ec9d29a0a8386612b622e4 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 10:46:27 -0800 Subject: [PATCH 13/25] Add functions for getting Set and List of deadlines from ReactionInstance --- .../lflang/generator/ReactionInstance.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index 92f15e362e..d13ce56fd1 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -357,6 +357,22 @@ public Set getLevels() { } return result; } + + /** + * Return a set of deadlines that runtime instances of this reaction have. + * A ReactionInstance may have more than one level if it lies within + * a bank and its dependencies on other reactions pass through multiports. + */ + public Set getInferredDeadlines() { + Set result = new LinkedHashSet<>(); + // Force calculation of levels if it has not been done. + parent.assignLevels(); + for (Runtime runtime : runtimeInstances) { + result.add(runtime.deadline); + } + return result; + } + /** * Return a list of levels that runtime instances of this reaction have. @@ -373,6 +389,23 @@ public List getLevelsList() { } return result; } + + /** + * Return a list of levels that runtime instances of this reaction have. + * The size of this list is the total number of runtime instances. + * A ReactionInstance may have more than one level if it lies within + * a bank and its dependencies on other reactions pass through multiports. + */ + public List getInferredDeadlinesList() { + List result = new LinkedList<>(); + // Force calculation of levels and deadlines if it has not been done. + parent.assignLevels(); + for (Runtime runtime : runtimeInstances) { + result.add(runtime.deadline); + } + return result; + } + /** * Return the name of this reaction, which is 'reaction_n', From 5733906c11b72ffa10dffe5200f8482f622ea42c Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 10:47:44 -0800 Subject: [PATCH 14/25] Fix mistake in assignInferredDeadlines --- .../src/org/lflang/generator/ReactionInstanceGraph.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index c5403c89fb..35e9c7cc66 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -92,6 +92,7 @@ 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 this.clear(); addNodesAndEdges(main); @@ -316,7 +317,6 @@ private void assignInferredDeadlines() { List start = new ArrayList<>(leafNodes()); // All leaf nodes have deadline initialized to their declared deadline or MAX_VALUE - while (!start.isEmpty()) { Runtime origin = start.remove(0); Set toRemove = new LinkedHashSet<>(); @@ -334,7 +334,7 @@ private void assignInferredDeadlines() { } // Remove visited edges. for (Runtime upstream : toRemove) { - removeEdge(upstream, origin); + removeEdge(origin, upstream); // If the upstream node has no more outgoing edges, // then move it in the start set. if (getDownstreamAdjacentNodes(upstream).size() == 0) { @@ -345,7 +345,6 @@ private void assignInferredDeadlines() { // Remove visited origin. removeNode(origin); } - } /** From 71b05919d09a2b8537f49cd5f495ddbc48ef83a5 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 10:48:29 -0800 Subject: [PATCH 15/25] Fix code generation. Get deadlines in same way as levels are got. --- .../generator/c/CTriggerObjectsGenerator.java | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 31b7ec351f..748d30a29e 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -345,20 +345,28 @@ private static boolean setReactionPriorities( // Force calculation of levels if it has not been done. reactor.assignLevels(); - // If any reaction has multiple levels, then we need to create - // an array with the levels here, before entering the iteration over banks. + // 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 var prolog = new CodeBuilder(); var epilog = new CodeBuilder(); for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { - var levels = r.getLevels(); - if (levels.size() != 1) { + var levelSet = r.getLevels(); + var deadlineSet = r.getInferredDeadlines(); + + if (levelSet.size() != 1 || deadlineSet.size() != 1) { if (prolog.length() == 0) { prolog.startScopedBlock(); epilog.endScopedBlock(); } + // Get inferredDeadlines and map to string representations + var deadlines = r.getInferredDeadlinesList().stream() + .map(elt -> ("0x" + Long.toString(elt.toNanoSeconds(), 16) + "LL")) + .collect(Collectors.toList()); + // 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, ", ")+" };"); } } } @@ -369,18 +377,21 @@ private static boolean setReactionPriorities( for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { foundOne = true; - // The most common case is that all runtime instances of the - // reaction have the same level, so deal with that case - // specially. - var levels = r.getLevels(); - if (levels.size() == 1) { + // 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 : levels) { + for (Integer l : levelSet) { level = l; } - var inferredDeadline = r.getRuntimeInstances().get(0).deadline; + + var inferredDeadline = TimeValue.MAX_VALUE; + for (TimeValue t : deadlineSet) { + inferredDeadline = t; + } // xtend doesn't support bitwise operators... var indexValue = inferredDeadline.toNanoSeconds() << 16 | level; @@ -394,19 +405,16 @@ private static boolean setReactionPriorities( CUtil.reactionRef(r)+".index = "+reactionIndex+";" )); } else { - // This instance belongs to a bank of Reactors. - // FIXME: We need a way to get the bank_idx of the reactor containing this reaction - // AFAICS its the only way to get the right RunTime and thus the right deadline - var bankIdx=1; - var inferredDeadline = r.getRuntimeInstances().get(bankIdx); - - var reactionDeadline = "0x" + Long.toString(inferredDeadline.toNanoSeconds(), 16) + "LL"; + // 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()); temp.pr(String.join("\n", CUtil.reactionRef(r)+".chain_id = "+r.chainID+";", - "// index is the OR of levels["+CUtil.runtimeIndex(r.getParent())+"] and ", - "// deadline "+r.inferredDeadline.toNanoSeconds()+" shifted left 16 bits.", - CUtil.reactionRef(r)+".index = ("+reactionDeadline+" << 16) | "+r.uniqueID()+"_levels["+CUtil.runtimeIndex(r.getParent())+"];" + "// index is the OR of levels["+runtimeIdx+"] and ", + "// deadlines["+runtimeIdx+"] shifted left 16 bits.", + CUtil.reactionRef(r)+".index = ("+r.uniqueID()+"_inferred_deadlines["+runtimeIdx+"] << 16) | " + + r.uniqueID()+"_levels["+runtimeIdx+"];" )); } } From e6378e372706d60c3404ef863fb2a433c96771f5 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 10:49:01 -0800 Subject: [PATCH 16/25] Add new test program for Banked reactors --- .../org/lflang/generator/ReactorInstance.java | 1 + test/C/src/DeadlineWithBanks.lf | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/C/src/DeadlineWithBanks.lf diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index ae953e9370..46bcf58fdd 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -182,6 +182,7 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re * @return An empty graph if successful and otherwise a graph * with runtime reaction instances that form cycles. */ + // FIXME: Now this function also assigns deadlines to the runtime instances public ReactionInstanceGraph assignLevels() { if (depth != 0) return root().assignLevels(); if (cachedReactionLoopGraph == null) { diff --git a/test/C/src/DeadlineWithBanks.lf b/test/C/src/DeadlineWithBanks.lf new file mode 100644 index 0000000000..266208cdac --- /dev/null +++ b/test/C/src/DeadlineWithBanks.lf @@ -0,0 +1,63 @@ +/** + * This program verifies that reactions inside banks inherit the correct + * priority from their downstream reactions. A global variable is used to check + * execution order. Threading is disabled + */ +target C { + threading: false, + timeout: 300 msec, + build-type: Debug +} + +preamble {= volatile int global_cnt = 0; =} + +reactor Bank(bank_index: int(0)) { + timer t(0, 100 msec) + output out: int + + reaction(t) -> out {= + int exp_cnt; + switch(self->bank_index) { + case 0: { + exp_cnt = 3; + break; + } + case 1: { + exp_cnt = 1; + break; + } + case 2: { + exp_cnt = 0; + break; + } + case 3: { + exp_cnt = 2; + break; + } + } + if (global_cnt != exp_cnt) { + lf_print_error_and_exit("global_cnt=%i expected=%i\n", global_cnt, exp_cnt); + } + global_cnt++; + + if (self->bank_index==0) { + global_cnt=0; + } + =} +} + +reactor Sink(dead: time(0)) { + input in: int + + reaction(in) {= =} deadline(dead) {= =} +} + +main reactor { + rp = new[4] Bank() + s1 = new Sink(dead = 14 msec) + s2 = new Sink(dead = 12 msec) + s3 = new Sink(dead = 11 msec) + s4 = new Sink(dead = 13 msec) + + rp.out -> s1.in, s2.in, s3.in, s4.in +} From ca43fe1e3261fa7b5f7d129f171178077786b24a Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Sun, 6 Nov 2022 16:01:02 -0800 Subject: [PATCH 17/25] Increase deadline to 100sec to fix macOS CI --- test/C/src/DeadlineInherited.lf | 2 +- test/C/src/DeadlinePriority.lf | 2 +- test/C/src/DeadlineWithAfterDelay.lf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/C/src/DeadlineInherited.lf b/test/C/src/DeadlineInherited.lf index 3cf13e92c1..eab2f56d9b 100644 --- a/test/C/src/DeadlineInherited.lf +++ b/test/C/src/DeadlineInherited.lf @@ -22,7 +22,7 @@ reactor WithDeadline { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } global_cnt--; - =} deadline(100 msec) {= =} + =} deadline(100 sec) {= =} } main reactor { diff --git a/test/C/src/DeadlinePriority.lf b/test/C/src/DeadlinePriority.lf index 09d710b035..723a29b754 100644 --- a/test/C/src/DeadlinePriority.lf +++ b/test/C/src/DeadlinePriority.lf @@ -20,7 +20,7 @@ reactor WithDeadline { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } global_cnt--; - =} deadline(100 msec) {= =} + =} deadline(100 sec) {= =} } main reactor { diff --git a/test/C/src/DeadlineWithAfterDelay.lf b/test/C/src/DeadlineWithAfterDelay.lf index c02b22f699..d1b87860ac 100644 --- a/test/C/src/DeadlineWithAfterDelay.lf +++ b/test/C/src/DeadlineWithAfterDelay.lf @@ -24,7 +24,7 @@ reactor SinkWithDeadline { if (global_cnt != 0) { lf_print_error_and_exit("Deadline reaction was not executed first. cnt=%i", global_cnt); } - =} deadline(100 msec) {= =} + =} deadline(100 sec) {= =} } main reactor { From e7ad63c9fd94b05103253f30be073d9be51c7f32 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 29 Nov 2022 15:27:28 -0800 Subject: [PATCH 18/25] Detect if there are deadlines globally. Assign deadlines only if there are deadlines. Handle all 4 cases of hasLevels and hasDeadlines --- .../org/lflang/generator/GeneratorBase.java | 6 ++ .../org/lflang/generator/GeneratorUtils.java | 1 + .../lflang/generator/ReactionInstance.java | 1 + .../generator/ReactionInstanceGraph.java | 9 ++- .../org/lflang/generator/ReactorInstance.java | 17 +++++ .../org/lflang/generator/c/CGenerator.java | 3 + .../generator/c/CTriggerObjectsGenerator.java | 64 +++++++++++++------ 7 files changed, 78 insertions(+), 23 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index debfcd8334..b3a9607bae 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -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. /** diff --git a/org.lflang/src/org/lflang/generator/GeneratorUtils.java b/org.lflang/src/org/lflang/generator/GeneratorUtils.java index 3dd2ac2496..959405e4af 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorUtils.java +++ b/org.lflang/src/org/lflang/generator/GeneratorUtils.java @@ -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; diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index d13ce56fd1..fdff6f6635 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -351,6 +351,7 @@ public Set dependsOnReactions() { public Set getLevels() { Set result = new LinkedHashSet<>(); // Force calculation of levels if it has not been done. + // FIXME: Is it necessary to repeat this everywhere? parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.level); diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index 35e9c7cc66..f2edcde611 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -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(); } /* diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 46bcf58fdd..182f075c32 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -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 + * 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; + + } /** * Return the instance of a child rector created by the specified diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index ec2b7986c3..ce2dd282de 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -2571,6 +2571,9 @@ private void createMainReactorInstance() { errorReporter.reportError("Main reactor has causality cycles. Skipping code generation."); return; } + if (hasDeadlines) { + this.main.assignDeadlines(); + } // Inform the run-time of the breadth/parallelism of the reaction graph var breadth = reactionInstanceGraph.getBreadth(); if (breadth == 0) { diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 748d30a29e..8630723c5d 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -343,10 +343,15 @@ 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? 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 + var prolog = new CodeBuilder(); var epilog = new CodeBuilder(); for (ReactionInstance r : reactor.reactions) { @@ -354,19 +359,26 @@ private static boolean setReactionPriorities( 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, ", ")+" };"); } } } @@ -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; @@ -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+" << 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 ", From 794de2cc22f23ff2ff4be8d12717b550c9c152f2 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 29 Nov 2022 15:43:54 -0800 Subject: [PATCH 19/25] Fix typo --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 8630723c5d..a080cee26c 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -431,7 +431,7 @@ private static boolean setReactionPriorities( 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+" << 16) | " + + CUtil.reactionRef(r)+".index = ("+inferredDeadline.toNanoSeconds()+" << 16) | " + r.uniqueID()+"_levels["+runtimeIdx+"];" )); From 02301f15bf45e18bfd6dd6d7bb8ebd04d9f10892 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 29 Nov 2022 15:50:08 -0800 Subject: [PATCH 20/25] Fix some comments --- .../src/org/lflang/generator/ReactionInstanceGraph.java | 5 ++++- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index f2edcde611..fbefe1459d 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -93,7 +93,10 @@ public void rebuild() { // throw new InvalidSourceException("Reactions form a cycle!"); } } - + /** + * This function rebuilds the graph and propagates and assigns deadlines + * to all reactions. + */ public void rebuildAndAssignDeadlines() { this.clear(); addNodesAndEdges(main); diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index a080cee26c..ba8d27db84 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -389,9 +389,6 @@ private static boolean setReactionPriorities( for (ReactionInstance r : reactor.reactions) { if (currentFederate.contains(r.getDefinition())) { foundOne = true; - - // The most common case is that all runtime instances of the - // reaction have the same level and deadline var levelSet = r.getLevels(); var deadlineSet = r.getInferredDeadlines(); From 53fd90e85829d0f8fe00ed87753cd831262d8668 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 29 Nov 2022 18:21:09 -0800 Subject: [PATCH 21/25] Address Edwards comments --- org.lflang/src/org/lflang/generator/ReactionInstance.java | 1 - org.lflang/src/org/lflang/generator/ReactorInstance.java | 2 -- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 1 - 3 files changed, 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index fdff6f6635..d13ce56fd1 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -351,7 +351,6 @@ public Set dependsOnReactions() { public Set getLevels() { Set result = new LinkedHashSet<>(); // Force calculation of levels if it has not been done. - // FIXME: Is it necessary to repeat this everywhere? parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.level); diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 182f075c32..2dc20b74b2 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -182,7 +182,6 @@ public ReactorInstance(Reactor reactor, ReactorInstance parent, ErrorReporter re * @return An empty graph if successful and otherwise a graph * with runtime reaction instances that form cycles. */ - // FIXME: Now this function also assigns deadlines to the runtime instances public ReactionInstanceGraph assignLevels() { if (depth != 0) return root().assignLevels(); if (cachedReactionLoopGraph == null) { @@ -205,7 +204,6 @@ public ReactionInstanceGraph assignDeadlines() { } cachedReactionLoopGraph.rebuildAndAssignDeadlines(); return cachedReactionLoopGraph; - } /** diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index ba8d27db84..532e7c9aa7 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -343,7 +343,6 @@ 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? reactor.assignLevels(); // We handle four different scenarios From 3c95857768532627f8a010e204eadf9bb5df73c8 Mon Sep 17 00:00:00 2001 From: erling Date: Tue, 29 Nov 2022 22:39:18 -0800 Subject: [PATCH 22/25] Apply suggestions from code review Co-authored-by: Marten Lohstroh --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 532e7c9aa7..a15b994f46 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -349,7 +349,7 @@ private static boolean setReactionPriorities( // 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 + // 4) Multiple deadlines and levels var prolog = new CodeBuilder(); var epilog = new CodeBuilder(); From 26b861b91ec3a6e19275bbfacb37673e3a0d1c73 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Tue, 29 Nov 2022 22:40:28 -0800 Subject: [PATCH 23/25] Address Martens comments --- .../src/org/lflang/generator/ReactionInstance.java | 12 +++--------- .../src/org/lflang/generator/ReactorInstance.java | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index d13ce56fd1..4c4e2abd6a 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -360,13 +360,10 @@ public Set getLevels() { /** * Return a set of deadlines that runtime instances of this reaction have. - * A ReactionInstance may have more than one level if it lies within - * a bank and its dependencies on other reactions pass through multiports. + * A ReactionInstance may have more than one deadline if it lies within. */ public Set getInferredDeadlines() { Set result = new LinkedHashSet<>(); - // Force calculation of levels if it has not been done. - parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.deadline); } @@ -391,15 +388,12 @@ public List getLevelsList() { } /** - * Return a list of levels that runtime instances of this reaction have. + * Return a list of the deadlines that runtime instances of this reaction have. * The size of this list is the total number of runtime instances. - * A ReactionInstance may have more than one level if it lies within - * a bank and its dependencies on other reactions pass through multiports. + * A ReactionInstance may have more than one deadline if it lies within */ public List getInferredDeadlinesList() { List result = new LinkedList<>(); - // Force calculation of levels and deadlines if it has not been done. - parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.deadline); } diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index 2dc20b74b2..782e16c2b3 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -191,10 +191,10 @@ public ReactionInstanceGraph assignLevels() { } /** - * This function assigns/propagates deadlines through the Reaction Instance Graph - * it performs Kahn`s algorithm in reverse, starting from the leaf nodes and + * 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 - * there are user-specified deadlines in the program + * there are user-specified deadlines in the program. * @return */ public ReactionInstanceGraph assignDeadlines() { From 1c95d0e16bee9e8a9cd1e8375be277e5cd822b62 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Wed, 7 Dec 2022 10:54:45 -0800 Subject: [PATCH 24/25] Remove redundant(?) calls to assignLevels --- .../src/org/lflang/generator/ReactionInstance.java | 8 ++++++-- .../org/lflang/generator/c/CTriggerObjectsGenerator.java | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstance.java b/org.lflang/src/org/lflang/generator/ReactionInstance.java index 4c4e2abd6a..cfcfe7f663 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstance.java @@ -351,7 +351,9 @@ public Set dependsOnReactions() { public Set getLevels() { Set result = new LinkedHashSet<>(); // Force calculation of levels if it has not been done. - parent.assignLevels(); + // FIXME: Comment out this as I think it is redundant. + // If it is NOT redundant then deadline propagation is not correct + // parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.level); } @@ -380,7 +382,9 @@ public Set getInferredDeadlines() { public List getLevelsList() { List result = new LinkedList<>(); // Force calculation of levels if it has not been done. - parent.assignLevels(); + // FIXME: Comment out this as I think it is redundant. + // If it is NOT redundant then deadline propagation is not correct + // parent.assignLevels(); for (Runtime runtime : runtimeInstances) { result.add(runtime.level); } diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index a15b994f46..705e263f00 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -178,7 +178,10 @@ public static String generateSchedulerInitializer( return ""; } var code = new CodeBuilder(); - var numReactionsPerLevel = main.assignLevels().getNumReactionsPerLevel(); + // FIXME: Comment out this as I think it is redundant. to assignLevels again + // If it is NOT redundant then deadline propagation is not correct + // var numReactionsPerLevel = main.assignLevels().getNumReactionsPerLevel(); + var numReactionsPerLevel = main.getNumReactionsPerLevel(); var numReactionsPerLevelJoined = Arrays.stream(numReactionsPerLevel) .map(String::valueOf) .collect(Collectors.joining(", ")); @@ -343,7 +346,9 @@ private static boolean setReactionPriorities( ) { var foundOne = false; // Force calculation of levels if it has not been done. - reactor.assignLevels(); + // FIXME: Comment out this as I think it is redundant. + // If it is NOT redundant then deadline propagation is not correct + // reactor.assignLevels(); // We handle four different scenarios // 1) A reactionInstance has 1 level and 1 deadline From a9c7a7ea3e0624375ad719cc75fcde8153f79e37 Mon Sep 17 00:00:00 2001 From: Erling Rennemo Jellum Date: Wed, 7 Dec 2022 13:00:03 -0800 Subject: [PATCH 25/25] Undo mistake --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 705e263f00..d28df9a249 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -178,10 +178,7 @@ public static String generateSchedulerInitializer( return ""; } var code = new CodeBuilder(); - // FIXME: Comment out this as I think it is redundant. to assignLevels again - // If it is NOT redundant then deadline propagation is not correct - // var numReactionsPerLevel = main.assignLevels().getNumReactionsPerLevel(); - var numReactionsPerLevel = main.getNumReactionsPerLevel(); + var numReactionsPerLevel = main.assignLevels().getNumReactionsPerLevel(); var numReactionsPerLevelJoined = Arrays.stream(numReactionsPerLevel) .map(String::valueOf) .collect(Collectors.joining(", "));