Compound function
AFCL introduces a rich set of control-flow constructs (compound functions)
to simplify the specification of realistic FCs that are difficult to be composed
with any current FC system without support by a skilled software developer.
Compound functions contain inner functions, which can be base or compound
and they are executed in the order defined by the compound function. The
inner functions are called children functions of the compound function. The
compound function is called the parent function of the inner functions. An inner
function of a compound function fi can be another compound or base function
fj . The term child function of fi refers to the entire compound function fj.
AFCL introduces the following compound functions: sequence, if-then-else,
switch, for, while, parallel, and parallelFor.
The specifications for the
name attribute, dataIns and dataOuts ports, along with the corresponding
source and saveto attributes are similar as for a base function. In the remain
der of this text, we will not separately explain the attributes of a compound
function and when we use the term function, it can refer to either base function
or compound function.
Sequence
The sequence compound function represents a sequential controlflow of all inner
functions within the sequenceBody section. In order to simplify
the AFCL, we assume that all base or compounds functions, which are specified
one after the other, are sequential without specifying them into a sequence
compound function.
AFCL introduces source attribute for dataOuts ports for each compound
function in order to specify internal data-flow from dataOuts ports of children
functions to dataOuts ports of the parent compound function. The value of
the source attribute is similar to that of the source attribute of a dataIns
port, except that it refers to dataOut ports of children functions of a compound
function.
sequence: {
name: "name",
dataIns: [{}+]?,
sequenceBody: [
{
function: {}+
}
],
dataOuts: [
{
name: "name",
type: "type",
saveto: "saveto"?
source: "source",
}+
]?
}
If-then-else
The if-then-else compound function is one of two conditional compound functions of AFCL.
The condition attribute describes a set of subconditions combined with the boolean combinedWith attribute. A sub-condition
contains data1 and data2 which represent the data to be compared according
to the value of the operator (==, <, >, ≤, ≥, and !=, contains, startsWith
and endsWith) and optionally negation. The values of data1 and data2 can
be constants or the output from previous functions. If the condition is satisfied
then functions within the then part are executed, otherwise the else branch is
executed.
if: {
name: "name",
dataIns: [{}+]?,
condition:
{
combinedWith: "and/or",
conditions: [
{
data1: "data1",
data2: "data2",
operator: "operator",
negation: "negation",
}+
]
},
then: [{function: {}}+],
else: [{function: {}}+]?,
dataOuts: [{}+]?
}
Switch
The switch compound function can be used to select a single
case or a default compound function depending on the value of the dataEval
attribute, thereby acting as an XOR logical expression. In case break is not
used, then the switch compound function acts as an OR logical expression and
multiple case branches might be selected.
switch: {
name: "name",
dataIns: [{}+]?,
dataEval:
{
name: "name",
type: "type",
source: "source"?
},
cases: [
{
value: "value",
break: "true"?,
functions: [{function: {}}+]
}+
],
default: [{function: {}}+]?,
dataOuts: [{}+]?
}
For
The for compound function executes its loopBody multiple times
based on the specified loopCounter. The value of the loopCounter is initially
set to the value specified by the attribute from and is then increased by the
value of step until it reaches the value of to or larger. The attributes from,
to, and step can be specified with a constant value or with data ports of other
functions. To express dependencies across loop iterations the dataLoop ports
are used. These ports get their initial value from the optional initSource field or a constant
value from the value field. A loopSource field specifies a data-flow from the
output of a function of the loop body which can be used as input to functions
executed in the next loop iteration. name is an unique identifier of a DataLoop
port and type specifies the data type of the value.
for: {
name: "name",
dataIns: [{}+]?,
dataLoops: [
{
name: "name",
type: "type",
initSource: "source",?
loopSource: "source",
value: "constant"?
}+
]?,
loopCounter:
{
name: "name",
type: "type",
from: "from",
to: "to",
step: "step"?,
},
loopBody: [{function: {}}+],
dataOuts: [{}+]?
}
While
The while compound function is used to execute a loopBody zero
or more times, depending on the specified condition. The condition has the
same structure as in the if-then-else compound.
The loopBody will be executed until the specified condition evaluates to false.
Similarly as in the for compound function, dependencies across loop iterations
in while can be expressed with dataLoop ports.
while: {
name: "name",
dataIns: [{}+]?,
dataLoops: [
{
name: "name",
type: "type",
initSource: "source",?
loopSource: "source",
value: "constant"?
}+
]?,
condition:
{
combinedWith: "and/or",
conditions: [{}+]
},
loopBody: [{function: {}}+],
dataOuts: [{}+]?
}
Parallel
The parallel compound function expresses the parallel execution
of a set of sections. Each section within the parallelBody represents a list of
base or a compound functions, which can run in parallel with other sections. The
parallel compound function can have arbitrary many data input ports, whose
associated data can be distributed among inner functions.
parallel: {
name: "name",
dataIns: [{}+]?,
parallelBody: [
{
section: [{function: {}}+]
}+
],
dataOuts: [{}+]?
}
ParallelFor
The parallelFor compound function expresses the simultaneous
execution of all loop iterations. It is assumed that there are no data dependen
cies across loop iterations. All other elements of the constructs behave the same
as in the for compound function.
parallelFor: {
name: "name",
dataIns: [{}+]?,
loopCounter:
{
name: "name", type: "type",
from: "from", to: "to",
step: "step"?,
},
loopBody: [{function: {}}+],
dataOuts: [{}+]?
}
Properties and Constraints
Properties and constraints are optional attributes defined within the
AFCL language, which provide additional information about dataIn ports,
dataOut ports, and base and compound functions.
Properties can be used to describe hints about the behaviour of functions,
e.g. expected size of input data or memory required for execution. Constraints
(e.g. finish execution time within a time limit, data distributions, fault tolerance
settings) should be fulfilled by the runtime system on a best-effort basis.
function: {
name: "name",
type: "type",
dataIns: [
{
name: "name", type: "type",
source: "source"?, value: "value"?,
properties: [{ name: "name", value: "value" }+]?,
constraints: [{ name: "name", value: "value" }+]?
}+
]?,
properties: [{ name: "name", value: "value" }+]?,
constraints: [{ name: "name", value: "value" }+]?,
dataOuts: [
{
name: "name", type: "type", saveto: "saveto"?,
properties: [{ name: "name", value: "value" }+]?,
constraints: [{ name: "name", value: "value" }+]?
}+
]?
}
Invocation type
The invoke-type is a built-in Property defined in AFCL. This Property can be
used to specify whether a base function should run
asynchronously (ASYNC) or synchronously (SYNC). An invoker of a synchronous
function waits for the function to finish (if the function has output data then
until the data arrived). With asynchronous invocation, the function will be
queued without waiting for the function to be finished. For example, the runtime
system can invoke a synchronous ”checker” function periodically to examine
whether the output of an asynchronous function is stored in a specified storage
(e.g. S3).
By default, if the invoke-type property is defined within a compound function,
all nested base functions within that compound function will inherit this
property and are invoked with the specified invoke-type. Otherwise, the base
function will be invoked as specified invoke-type property. If ASYNC is specified,
the FC designer must guarantee that the FC still operates correctly. Without
specifying any invoke-type in any of the parent compound functions, the base
functions are executed synchronously in AFCL.
The build-in function asyncHandler is used to handle ASYNC invoked functions.
This function can be used the same way as a base function is used, while
the type field specifies that it is a build-in function. The build-in function
has one input parameter, representing a coma separated list of names of ASYNC
invoked functions (e.g. FunctionName1) and one boolean output parameter
which represents whether all of these invoked functions finished. asyncHandler
can be invoked with invoke-type i) ASYNC, meaning that asyncHandler immediately
returns with the output parameter set to true if all functions (specified
in the input parameter) finished otherwise it is set to false, or ii) SYNC,
meaning that asyncHandler waits for all functions (specified in the input parameter)
to finish before it returns
Base function
function: {
name: "name", type: "type",
dataIns: [{}+]?,
properties: [
{
name: "invoke-type",
value: "ASYNC | SYNC"
}+
]?,
dataOuts: [{}+ ]?
}
Compound function
function: {
name: "name", type: "build-in:asyncHandler",
dataIns: [
{
name: "name", type: "collection",
value: "FunctionName1,FunctionName2,...,FunctionNameN"
}+
],
properties: [
{
name: "invoke-type",
value: "ASYNC | SYNC"
}+
]?,
dataOuts: [
{
name: "name", type: "boolean"
}+
]
}