IDS is a genom object that can be defined within a
component in order to store and share private data among different services and
codels of a component. Private data, in this context, means data that is not
exported nor visible from the component interface, but used internally to store
state, temporary computation data, etc. It is similar to the purpose of the
private
C++
keyword.
Private data can be defined in several ways, depending on the expected usage of the data. When deciding whether to define an IDS, different scenario can be considered:
In this case, declaring static
data in the source file where the service
codels are implemented is sufficient. Since no two codels of the same service
will ever execute simultaneously, no locking or any synchronization strategies
need to be implemented. This is the simplest scenario.
a static definition enforces the private nature of the
data. Technically, a global definition would also work, just providing less
guarantees regarding the visibility of data.
|
Declaring static
data in the source file where all the codels of the services
of the task are defined is also sufficient in this case. No two codels of the
same task can execute simultaneously (by definition of a task), so no
synchronization strategies is required.
Codels of different tasks (including codels of functions and attributes) can
execute simultaneously. In order to share data between them, a mere global or
static
definition for the data is not sufficient and proper synchronization
between the codels must be achieved. This is where the IDS definition becomes
handy, as it handles the synchronization transparently and in an efficient
manner, depending on which codel access which data.
In order to demonstate the IDS usage, let’s consider two simple services. The
first one, counter
will count from 0 some maximum value, at a fixed rate. The
second one, reset
will simply reset the current counter value so that
counter
starts again from 0. counter
must be implemented as an activity,
because it will iterate at a given frequency, while reset
may be a simple
function.
It should be obvious that in order to implement these two activities, they must
share the current counter value. This is easily achieved by using an IDS
declaration inside the component definition, that would look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
component example {
ids { long current; };
task t { period 1s; };
activity counter(in long max = 42: "Maximum counter value") {
doc "Count from 0 to max";
task t;
codel<start> counter_impl(in max, inout current) yield start, ether;
};
function reset() {
codel reset_impl(out current);
};
};
In line 2 of this example, the IDS is declared, along with its content, much
like a structure
definition. In this example, a simple integer variable
current
is declared.
On line 6 and 13, the two services are declared, with their
parameters. Activity counter
takes an integer to define the maximum counter
value, while reset
has no parameter.
The interesting parts are on lines 10 and 14, with the codels definitions. The
codel counter_impl
for the counter
activity needs two parameters: the max
value given as input to the activity, and the private variable current
, to be
updated at each step. The different nature of the two parameters are
automatically recognized by genom. The codel reset_impl
for the reset
function just need to zero the current
value from the IDS.
The implementation of these two codels could be the following:
genom_event
counter_impl(int32_t max, int32_t *current, const genom_context self)
{
(*current)++;
if (*current < max) return example_start; else return example_ether;
}
genom_event
reset_impl(int32_t *current, const genom_context self)
{
*current = 0;
return genom_ok;
}
The values of the IDS are not initialized to any special value when the component starts. Often, though, it is desirable to assign them reasonable values. This can be achieved by defined a task start codel, that takes a list of IDS members to initialize.
For example:
component example {
ids {
long value;
double parameter;
string data;
};
task t {
codel<start> init_ids(out value, out parameter, out data) yield ether;
};
};
init_ids
will be called during the component startup and can thus initialize
the designated members, for instance like this:
genom_event
init_ids(int32_t *value, double *parameter, char **data,
const genom_context self)
{
*value = 0;
*parameter = 3.14;
*data = strdup("a string ...");
return example_ether;
}
When the IDS has many members, it might be more convenient to pass the whole
IDS as a single parameter. This can be achieved by using the parameter::name
syntax, with an empty parameter
part and a name of your liking:
component example {
ids {
long value;
double parameter;
string data;
};
task t {
codel<start> init_ids(out ::whole_ids) yield ether;
};
};
init_ids
will then be passed a pointer on the whole IDS structure:
genom_event
init_ids(example_ids *whole_ids, const genom_context self)
{
whole_ids->value = 0;
whole_ids->parameter = 3.14;
whole_ids->data = strdup("a string ...");
return example_ether;
}
As it was shown in the previous paragraphs, GenoM automatically recognize the codel parameter source when the parameter name is not ambiguous. However, it can happen that a member of the IDS has the same name as a service parameter (or a port name).
For instance, the following example is ambiguous and leads to some compilation errors:
component example {
ids { long value; };
function set_value(in long value) {
codel set_value(in value, out value); /* this is ambiguous */
};
};
$ genom3 -n example.gen
example.gen:5: missing source qualifier for parameter 'value'
example.gen:2: ids member value declared here
example.gen:4: service parameter value declared here
example.gen:5: missing source qualifier for parameter 'value'
example.gen:2: ids member value declared here
example.gen:4: service parameter value declared here
genom3: 2 errors
$
In order to distinguish between the value
parameeter of the function
set_value
and the IDS member value
, two changes must be done to the
previous example.
First, the codel set_value
must indicate the origin of each value, using
one of the ids
, local
and port
keyword. ids
and port
designate a
parameter from the IDS or a port name, respectively, while local
refers to
the service parameters.
Then, since a codel cannot have two parameters with the same name, one of
the parameters (or both) must be given another local name, by using the
parameter::name
syntax.
This would be written like so in the component description file:
component example {
ids { long value; };
function set_value(in long value) {
codel set_value(local in value, ids out value::ids_value);
};
};