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

Adding support of transient federates #2213

Draft
wants to merge 63 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
b7104b4
Add isTransient member to a federated instance and parse the transien…
ChadliaJerad Jan 11, 2024
d01b2bf
Set the transient indicator
ChadliaJerad Jan 11, 2024
ccc6b6a
Make federates bin directory visible
ChadliaJerad Feb 20, 2024
5d23ac0
Add support of transient attribute
ChadliaJerad Jan 11, 2024
d49a7ed
Transient annotation is only allowed for federates + Leave fixmes abo…
ChadliaJerad Jan 11, 2024
4908b60
Align reactor-c
ChadliaJerad Feb 14, 2024
70ea192
Add the number of transients option to the fderation launcher script
ChadliaJerad Feb 19, 2024
55e5413
Fix script identation
ChadliaJerad Feb 19, 2024
cf39668
Add transients tests
ChadliaJerad Feb 20, 2024
b311402
Align reactor-c
ChadliaJerad Feb 20, 2024
bed73cf
Apply spotless
ChadliaJerad Feb 20, 2024
41ba62a
Align reactor-c
ChadliaJerad Feb 20, 2024
f45ae31
Apply spotless
ChadliaJerad Feb 20, 2024
e18eb02
Fix accidentally removed line from FederateInstance.java
ChadliaJerad Feb 21, 2024
eb6fe81
Update core/src/main/java/org/lflang/federated/launcher/FedLauncherGe…
ChadliaJerad Feb 22, 2024
3c6802d
Update test/C/src/federated/transient/TransientHotSwap.lf
ChadliaJerad Feb 22, 2024
0989e72
Address review by removing FIXMEs and open an issue instead
ChadliaJerad Feb 22, 2024
1bc4a6c
Address review by removing period from TransientExec in transients te…
ChadliaJerad Feb 22, 2024
3503ee1
Reduce the probability of flaky failiures due to indeterminate amount…
ChadliaJerad Feb 22, 2024
c7e1bef
Fix TransientHotSwap documentation based on review
ChadliaJerad Feb 22, 2024
8b8920c
Apply spotless
ChadliaJerad Feb 22, 2024
d0e0bdf
Fix TransientDownstreamWithTwoUpstreams error condition to account do…
ChadliaJerad Feb 22, 2024
86e4b90
Merge branch 'master' into transient-fed
ChadliaJerad Feb 29, 2024
094719f
Align reactor-c
ChadliaJerad Feb 29, 2024
b6d1a93
Add TransientStatePersistence test and align reactor-c
ChadliaJerad Feb 29, 2024
a1eef8d
Align reactor-c
ChadliaJerad Feb 29, 2024
a511cea
Apply Spotless
ChadliaJerad Feb 29, 2024
3b78d8c
Merge branch 'master' into transient-fed
ChadliaJerad Mar 15, 2024
d532508
Merge branch 'master' into transient-fed
ChadliaJerad Mar 15, 2024
bc17f00
Typo + Adjust message
ChadliaJerad Mar 18, 2024
4ea922c
Align reactor-c to transient-fed-use-pqueue
ChadliaJerad Mar 18, 2024
90c947c
Use lf_tag_effective_start() instead of lf_get_effective_start_tag()
ChadliaJerad Mar 18, 2024
b5cfe84
Fix comment
ChadliaJerad Mar 18, 2024
9335468
Align reactor-c (transient-fed-use-pqueue)
ChadliaJerad Mar 18, 2024
6610247
Minor updates to transient tests
ChadliaJerad Mar 18, 2024
316a809
Align reactor-c (transient-fed-use-pqueue)
ChadliaJerad Mar 18, 2024
9c5725f
Merge branch 'master' into transient-fed
ChadliaJerad Apr 12, 2024
aa94f96
Merge branch 'master' into transient-fed
ChadliaJerad Apr 15, 2024
7466603
Align reactor-c
ChadliaJerad Apr 15, 2024
e482e49
Align reactor-c
ChadliaJerad Apr 15, 2024
84a075c
Merge branch 'master' into transient-fed
ChadliaJerad Apr 24, 2024
9a53f26
Add authentication to TransientDownstreamWithTimer.lf test
ChadliaJerad Apr 24, 2024
56a2be1
Merge branch 'master' into transient-fed
ChadliaJerad May 8, 2024
c8f8315
TransientHotSwap test uses authentication + Align reactorC
ChadliaJerad May 18, 2024
ba63421
Align reator-c
ChadliaJerad May 18, 2024
5083f35
Merge branch 'master' into transient-fed
ChadliaJerad May 18, 2024
a6be23d
Merge branch 'master' into transient-fed
ChadliaJerad Jun 5, 2024
928067b
Fix the usage of authentication is transient tests
ChadliaJerad Jun 19, 2024
b72bfe1
Align reactor-c
ChadliaJerad Jun 19, 2024
43b7008
Merge branch 'master' into transient-fed
ChadliaJerad Jun 19, 2024
142930a
Align reactor-c
ChadliaJerad Jun 19, 2024
855fffa
Include LF_FEDERATES_BIN_DIRECTORY is cmake for federaes + Align reac…
ChadliaJerad Jun 20, 2024
e0b595e
Merge branch 'master' into transient-fed
ChadliaJerad Jun 21, 2024
49b79cf
Merge branch 'master' into transient-fed
ChadliaJerad Jul 24, 2024
b10515e
Merge branch 'master' into transient-fed
ChadliaJerad Aug 7, 2024
da29219
Merge branch 'master' into transient-fed
ChadliaJerad Aug 14, 2024
d49fba3
Merge branch 'master' into transient-fed
ChadliaJerad Aug 21, 2024
d0ee79f
Merge branch 'master' into transient-fed
ChadliaJerad Oct 1, 2024
970855d
Merge branch 'master' into transient-fed
ChadliaJerad Dec 2, 2024
917d259
Align reactor-c
ChadliaJerad Dec 6, 2024
918cd80
Merge branch 'master' into transient-fed
ChadliaJerad Dec 10, 2024
1a5a35d
Merge branch 'master' into transient-fed
ChadliaJerad Dec 11, 2024
fc6c4e3
Merge branch 'master' into transient-fed
ChadliaJerad Dec 16, 2024
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
5 changes: 5 additions & 0 deletions core/src/main/java/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ public static boolean isSparse(EObject node) {
return findAttributeByName(node, "sparse") != null;
}

/** Return true if the node has an {@code @transient} attribute. */
public static boolean isTransient(Instantiation node) {
return findAttributeByName(node, "transient") != null;
}

/** Return true if the reactor is marked to be a federate. */
public static boolean isFederate(Reactor reactor) {
return findAttributeByName(reactor, "_fed_config") != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ private String generateCodeToInitializeFederate(FederateInstance federate, RtiCo
}
// Set global variable identifying the federate.
code.pr("_lf_my_fed_id = " + federate.id + ";");
// Set indicator variable that specifies whether the federate is transient or not.
code.pr("_fed.is_transient = " + federate.isTransient + ";");

// We keep separate record for incoming and outgoing p2p connections to allow incoming traffic
// to be processed in a separate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.lflang.AttributeUtils;
import org.lflang.MessageReporter;
import org.lflang.TimeValue;
import org.lflang.ast.ASTUtils;
Expand Down Expand Up @@ -95,6 +96,7 @@ public FederateInstance(
this.bankWidth = bankWidth;
this.messageReporter = messageReporter;
this.targetConfig = targetConfig;
this.isTransient = AttributeUtils.isTransient(instantiation);

// If the instantiation is in a bank, then we have to append
// the bank index to the name.
Expand Down Expand Up @@ -156,6 +158,9 @@ public Instantiation getInstantiation() {
/** The integer ID of this federate. */
public int id;

/** Type of the federate: transient if true, and peristent if false . */
public boolean isTransient = false;

/**
* The name of this federate instance. This will be the instantiation name, possibly appended with
* "__n", where n is the bank position of this instance if the instantiation is of a bank of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,17 @@ private String getRtiCommand(List<FederateInstance> federates, boolean isRemote)
if (targetConfig.getOrDefault(TracingProperty.INSTANCE).isEnabled()) {
commands.add(" -t \\");
}
// Identify the transient federates number
ChadliaJerad marked this conversation as resolved.
Show resolved Hide resolved
int transientFederatesNumber = 0;
for (FederateInstance federate : federates) {
if (federate.isTransient) {
transientFederatesNumber++;
}
}
commands.addAll(
List.of(
" -n " + federates.size() + " \\",
" -nt " + transientFederatesNumber + " \\",
" -c "
+ targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE).toString()
+ " \\"));
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/lflang/generator/c/CCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,15 @@ private static List<String> cmakeOptions(TargetConfig targetConfig, FileConfig f
String maybeQuote = ""; // Windows seems to require extra level of quoting.
String srcPath = fileConfig.srcPath.toString(); // Windows requires escaping the backslashes.
String rootPath = fileConfig.srcPkgPath.toString();
String binPath = fileConfig.binPath.toString();
String srcGenPath = fileConfig.getSrcGenPath().toString();
if (separator.equals("\\")) {
separator = "\\\\\\\\";
maybeQuote = "\\\"";
srcPath = srcPath.replaceAll("\\\\", "\\\\\\\\");
rootPath = rootPath.replaceAll("\\\\", "\\\\\\\\");
srcGenPath = srcGenPath.replaceAll("\\\\", "\\\\\\\\");
binPath = binPath.replaceAll("\\\\", "\\\\\\\\");
}
arguments.addAll(
List.of(
Expand All @@ -245,6 +247,8 @@ private static List<String> cmakeOptions(TargetConfig targetConfig, FileConfig f
arguments.add("-DLF_SOURCE_DIRECTORY=\"" + maybeQuote + srcPath + maybeQuote + "\"");
arguments.add("-DLF_PACKAGE_DIRECTORY=\"" + maybeQuote + rootPath + maybeQuote + "\"");
arguments.add("-DLF_SOURCE_GEN_DIRECTORY=\"" + maybeQuote + srcGenPath + maybeQuote + "\"");
} else {
arguments.add("-DLF_FEDERATES_BIN_DIRECTORY=\"" + maybeQuote + binPath + maybeQuote + "\"");
}
arguments.add(FileUtil.toUnixString(fileConfig.getSrcGenPath()));

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/lflang/validation/AttributeSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ enum AttrParamType {
new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false))));
// @sparse
ATTRIBUTE_SPECS_BY_NAME.put("sparse", new AttributeSpec(null));
// @transient
ATTRIBUTE_SPECS_BY_NAME.put("transient", new AttributeSpec(null));
// @icon("value")
ATTRIBUTE_SPECS_BY_NAME.put(
"icon",
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/org/lflang/validation/LFValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,21 @@ public void checkInstantiation(Instantiation instantiation) {
error("Variable-width banks are not supported.", Literals.INSTANTIATION__WIDTH_SPEC);
}
}

// If the Instantiation is annotated as '@transient', then:
// - The container has to be a federated reactor,
// - The coordination is centralized,
// - And the target is C.
// FIXME: Conditions 2 and 3 need to be checked
// FIXME: Add support of transients in decentralized coordination.
ChadliaJerad marked this conversation as resolved.
Show resolved Hide resolved
if (AttributeUtils.isTransient(instantiation)) {
Reactor container = (Reactor) instantiation.eContainer();
if (!container.isFederated()) {
error(
"Only federates can be transients: " + instantiation.getReactorClass().getName(),
Literals.INSTANTIATION__REACTOR_CLASS);
}
}
}

@Check(CheckType.FAST)
Expand Down
158 changes: 158 additions & 0 deletions test/C/src/federated/transient/TransientDownstreamWithTimer.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* This LF program tests if a transient federate corretly leaves then joins the federation. It also
* tests if the transient's downstream executes as expected, that is it received correct TAGs,
* regardless of the transient being absent or present. In this test:
* - the transient federate spontaneously leaves the federation after 2 reactions to input port
* `in`,
* - the downstream of the transient federate has only one transient as upstream.
*/
target C {
timeout: 2 s
}

preamble {=
#include <stdlib.h>
#include <string.h>
=}

/** Persistent federate that is responsible for lauching the transient federate */
reactor TransientExec(offset: time = 0, period: time = 0, fed_instance_name: char* = "instance") {
ChadliaJerad marked this conversation as resolved.
Show resolved Hide resolved
timer t(offset, period)

reaction(t) {=
// Construct the command to launch the transient federate
char mid_launch_cmd[512];
sprintf(mid_launch_cmd,
"%s/federate__%s -i %s",
lf_get_federates_bin_directory(),
self->fed_instance_name,
lf_get_federation_id()
);

lf_print("Launching federate federate__%s at physical time " PRINTF_TIME ".",
mid_launch_cmd,
lf_time_physical());

int status = system(mid_launch_cmd);

// Exit if error
if (status == 0) {
lf_print("Successfully launched federate__%s.", self->fed_instance_name);
} else {
lf_print_error_and_exit("Unable to launch federate__%s. Abort!", self->fed_instance_name);
}
=}
}

/**
* Persistent federate, upstream of the transient. It reacts to its timer by sending increments to
* output port out.
*/
reactor Up(period: time = 500 ms) {
output out: int
timer t(0, period)
state count: int = 0

reaction(t) -> out {=
lf_set(out, self->count);
self->count++;
=}
}

/**
* Transient federate that forwards whatever it receives from `Up` to `Down`. It reacts twice to
* input port `in`, then stops. It will execute twice during the lifetime of the federation. The
* second launch is done by `TransientExec` at logical time 1 s. Each time `Middle` joins, it
* notifies `Down`.
*/
reactor Middle {
input in: int
output out: int
output join: int
state count: int = 0

// Middle notifies its downstream that he joined, but make sure first that the effective start
// tag is correct
reaction(startup) -> join {=
if(lf_get_effective_start_time() < lf_get_start_time()) {
lf_print_error_and_exit("Fatal error: the transient's effective start time is less than the federation start time");
}

lf_set(join, 0);
=}

// Pass the input value to the output port and stop spontaneously after two reactions to in
reaction(in) -> out {=
self->count++;
lf_set(out, in->value);

if (self->count == 2) {
lf_stop();
}
=}
}

/**
* Persistent federate, which is downstream of the transient. It has to keep reacting to its
* internal timer and also to inputs from the tansient, if any.
*/
reactor Down {
timer t(0, 500 ms)

input in: int
input join: int

state count_timer: int = 0
state count_join: int = 0
state count_in_mid_reactions: int = 0

reaction(t) {=
self->count_timer++;
=}

reaction(in) {=
self->count_in_mid_reactions++;
=}

reaction(join) {=
self->count_join++;
=}

reaction(shutdown) {=
// Check that the TAG has been successfully issued to Down
if (self->count_timer != 5) {
lf_print_error_and_exit("Federate's timer reacted %d times, while it had to react %d times.",
self->count_timer,
5);
}

// Check that `Middle` have joined 2 times
if (self->count_join != 2) {
lf_print_error_and_exit("Transient federate did not join twice, but %d times!", self->count_join);
}

// Check that `Middle` have reacted correctly
if (self->count_in_mid_reactions < 4) {
lf_print_error_and_exit("Transient federate Mid did not execute and pass values from up corretly! Expected >= 4, but had: %d.",
self->count_in_mid_reactions);
}
=}
}

federated reactor {
// Persistent federate that is responsible for lauching the transient once, after 1s
midExec = new TransientExec(offset = 1 s, fed_instance_name="mid")

// Persistent downstream and upstream federates of the transient
up = new Up()
down = new Down()

// Transient federate
@transient
mid = new Middle()

// Connections
up.out -> mid.in
mid.join -> down.join
mid.out -> down.in
}
Loading
Loading