Event handling

As already presented, the select statement allows code to wait for events to occur. It can wait for different events in different cases:

select {
  case A:
    ...
    break;
  case B:
    ...
    break;
  case C:
    ...
    break;
}

This section describes some advanced features of this event handling language construct.

Guards

When a select is waiting on several events, some events are conditionally enabled i.e. the code should only react to them if some guard expression evaluated to non-zero. The syntax for this is:

case expr => ... :

The following example only reacts to the timer event if the variable periodic_enabled is non-zero:

int periodic_enabled;
timer tmr;
uint32_t t;

...

select {
  case periodic_enabled => tmr when timerafter(t) :> void:
    ..
    break;
  case ...

}

If the case being guarded is an interface transaction, the compiler needs extra information to be able to implement the guard. To enable the compiler to do this, if an interface function is guarded anywhere in the program, it must be marked as possibly guarded in the interface declaration using the [[guarded]] attribute. For example:

interface if1 {
   void f();
   [[guarded]] void g();  // this function may be guarded in the program
}

..
select {
  case i.f():
     ...
     break;
  case e => i.g():
     ...
     break;
}

Ordering

Generally there is no priority on the events in a select. If more than one event is ready when the select executes, the chosen event is unspecified.

Sometimes it is useful to force a priority by using the [[ordered]] attribute which says that a select is presented with events ordered in priority from highest to lowest. For example, if all three events in the allowing example are ready at the time of the select, case A will be chosen:

[[ordered]]
select {
  case A:
    ...
    break;
  case B:
    ...
    break;
  case C:
    ...
    break;
}

Ordered selects cannot be used in combinable or distributable functions.

Defaults

Usually a select waits until one of the events occurs. It is possible to add a default case to a select. This will fire if none of the other events are ready when the select is first executed:

select {
  case A:
    ...
    break;
  case B:
    ...
    break;
  default:
    ...
    break;
}

Defaults cannot be used in combinable or distributable functions.

Replicated cases

Replicated cases iterate the same case several times. This is useful if the program has an array of resources to react to. For example, the following code iterates over an array of timers and associated timeouts.

timer    tmr_array[5];
uint32_t timeout[5]
unit32_t period[5];

...

select {
   case(size_t i = 0; i < 5; i++)
     tmr_array[i] when timerafter(timeout[i]) :> void:
        ....
        timeout[i] += period[i];
        break;
}

See Also