Menu

In the tutorial on the usage of the ‘blocking’ keyword, we implemented a situation where a synchronous event call was mapped on an asynchronous reply. The use of the ‘async’ keyword is to support the reciprocal situation: an asynchronous event call is mapped on a synchronous reply. As you can imagine this requires some dedicated internal processing. In normal situation an asynchronous event call returns and a new external trigger is required to eventually generate the asynchronous reply.

In this ‘async’ case the caller of the event should not notice it gets an immediate response. By some runtime magic the asynchronous reply event is generated automatically without requiring an external trigger.

This may sound a bit puzzling still but in the next example it will become clear what is happening.

The interface declares an asynchronous input event e and a (related) asynchronous output event a. The fact of the void reply value of the input event already indicates it cannot return anything synchronously.

In the component at the highlighted line a special internal port is declared as dzn.async. The name of the port is ‘defer’. The type dzn.async is defined inside Dezyne and it has a number of predefined functions. In this example we use:

  • req – this function queues an event in the Dezyne context that will be called after all processing of the current context has been completed. From an outside perspective it will be called after the current external event has been handled and before a next external event will be picked up. More on this in ‘async’ behaviour in runtime context.

  • ack – this is the event that will eventually be called as the consequence of the previous request.

In the declaration of the internal port we added a parameter ‘t’ of type ‘T’ that can be used in all functions of the port.

extern T $int$;

interface i
{
  in void e(T t);
  out void a(T t);
  behaviour {
    bool idle = true;
    [idle] on e: idle=false;
    [!idle] {
      on inevitable: {idle=true;a;}
      on e: illegal;
    }
  }
}

component async_hello
{
  provides i p;
  behaviour {
    requires dzn.async(T t) defer;
    bool idle = true;
    [idle] on p.e (t): {idle=false; defer.req (t);}
    [!idle] on defer.ack (t): {idle=true; p.a (t);}
  }
}

In this example the ‘defer.req’ puts an event in the queue. After all processing has completed (this may also involve handling input and output events to other components), the event call returns and subsequently the defer.ack event fires. This will trigger the actions listed at the ‘on defer.ack’ and as a result the output event a is called.

From the perspective of the caller, the event handling is asynchronous. Still this was all handled by the called component without the need for an external event. Therefore, the asynchronous protocol has successfully been made seemingly synchronous by using the ‘async’ keyword.

As you can see in the example the ‘defer.ack’ event is treated in the code as any other event.

image

The type dzn.async has one more function we did not discuss yet and we will show that in the next example:

  • clr – this function clears a previously queued event. It might happen that in the course of processing all input and output events something else raised the need to not use this internal trigger. Then the operation can be cancelled and ‘defer.ack’ will not be triggered.

In the example below the defer.clr is called as result from an event c. The situation where a clr needs to be called will typically be defined in the interplay between multiple components.

extern T $int$;

interface i
{
  in void e(T t);
  in void c(T t);
  out void a(T t);
  out void b();
  behaviour {
    bool idle = true;
    [idle] {
      on e: idle=false;
      on c: illegal;
    }
    [!idle] {
      on c: idle=true;
      on inevitable: {idle=true;a;b;}
      on e: illegal;
    }
  }
}
component async_cancel
{
  provides i p;
  behaviour {
    requires dzn.async(T t) defer;
    bool idle = true;
    [idle] on p.e (t): {idle=false; defer.req (t);}
    [!idle] on p.c (t): {idle=true; defer.clr ();}
    [!idle] on defer.ack (t): {idle=true;p.a (t);p.b();}
  }
}

We have now covered all the basics of the async keyword. In Applications of ‘async’ we will visit a few typical situations where the async pattern comes in handy.

Enjoy this article? Don't forget to share.