Project

General

Profile

IDS (Internal Data Structure)

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.

IDS versus static versus global

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:

Shared data between codels of the same service

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.

Note 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.

Shared data between codels of the same tasks

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.

Shared data between codels of different tasks

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.

IDS example

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:

example.gen
 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;
}

Initializing IDS data

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:

example.gen
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:

example.gen
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;
}

Name disambiguation

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:

example.gen
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:

example.gen
component example {
  ids { long value; };

  function set_value(in long value) {
    codel set_value(local in value, ids out value::ids_value);
  };
};