Contents

Dezyne: From dzn.async via Defer to Async

Introduction

Sometimes a synchronous interface needs to be converted into an asynchronous interface. The way to do this in Dezyne from Verum has changed over time. I will first introduce two interfaces, one asynchronous and one synchronous. Then I will show different ways to make a component that provides the synchronous and requires the asynchronous interfaces.

Unless stated otherwise, all examples are made using Dezyne 2.18.

From Synchronous to Asynchronous Calls

One of the often needed behavioral transformations is converting a synchronous interface to an asynchronous one. For the examples below I introduce two interfaces.

The ExampleControl has a Start in-event which can succeed or fail, indicated by an asynchronous OnOk or OnFail out-event.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
interface ExampleControl {
    in void Start();
    in void Cancel();
    in void Reset();
    out void OnOk();
    out void OnFail();

    behavior {
        enum State {
            Idle,
            Starting,
            Started
        };
        State state = State.Idle;

        [state.Idle]
        {
            on Start: state = State.Starting;
            on Cancel: {}
        }

        [state.Starting]
        {
            on Cancel: state = State.Idle;
            on optional: { OnOk; state = State.Started; }
            on inevitable: { OnFail; state = State.Idle; }
        }

        [state.Started]
        {
            on Reset: state = State.Idle;
        }
    }
}

The Example interface is the synchronous counterpart where the result of Start is given by the reply value of Result.Ok or Result.Fail.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum Result {
    Ok,
    Fail,
};

interface Example {
    in Result Start();
    in void Reset();

    behavior {
        bool x = true;

        enum State {
            Idle,
            Started
        };
        State state = State.Idle;

        [state.Idle] on Start:
        {
            [x] { state = State.Started; reply(Result.Ok); }
            [x] reply(Result.Fail);
        }
        [state.Started] on Reset: state = State.Idle;
    }
}

The x boolean defined on line 11 is used in the guards of Start to indicate non-determinism. Since x is always true, a [x] guard is the same as a [true] guard. I think it looks nice in the interface description to use the [x] guards. It mimics a checkbox list.

Dezyne 2.16: dzn.async and Why Not to Use It

In Dezyne 2.16 and before there was an internal dzn.async interface that could be used to create asynchronous behavior. It could pass along parameters and the ack out-event had a higher priority than other events. The effect was that the ack out-event would be processed before and not be interrupted by other events.

Below is the definition of the dzn.async interface in pseudo code. The formals on line 3 represents an optional list of external parameters. On line 15 the immediately trigger is used to indicate the higher priority of the ack out-event.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
namespace dzn {

interface async(formals) {
    in void req(formals);
    in void clr();

    out void ack(formals);

    behavior {
        bool armed = false;

        on clr: armed = false;
        [!armed] on req: armed = true;
        [armed] on immediately: { ack; armed = false; }
    }
}

}

Below is shown how to use dzn.async. It has been made using Dezyne 2.16. In this example we do not use the passing of parameters (formals). To not have to keep track of the result of the base.Start() call we do that call in the ack out-event handler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
component ExampleControlDznAsync {
    provides ExampleControl api;
    requires Example base;

    behavior {
        requires dzn.async async;

        enum State {
            Idle,
            Starting,
            Started
        };
        State state = State.Idle;

        [state.Idle]
        {
            on api.Start():
            {
                async.req();
                state = State.Starting;
            }

            on api.Cancel(): {}
        }

        [state.Starting]
        {
            on api.Cancel():
            {
                async.clr();
                state = State.Idle;
            }

            on async.ack():
            {
                Result res = base.Start();
                if (res.Ok)
                {
                    api.OnOk();
                    state = State.Started;
                }
                else
                {
                    api.OnFail();
                    state = State.Idle;
                }
            }
        }

        [state.Started]
        {
            on api.Reset():
            {
                base.Reset();
                state = State.Idle;
            }
        }
    }
}

Below is an image of a trace of this component. Here we see a downside of using dzn.async. It is not clear from the trace when req or ack are triggered, or even the existence of this internal component. You can see that the call to Start and the answer (OnOk or OnFail) are decoupled / asynchronous.

/posts/dezyne-async/dzn_async.png

The major advantage of dzn.async is not demonstrated in this example. That is that that the ack event has a higher priority than normal events. The effect of this is that the ack events are received before other events and that no behavior will have to be defined for those other events. This can reduce the amount of code that needs to handle the different interleavings of events.

This advantage was also the main disadvantage. The semantics is not compatible with the blocking feature. The blocking feature is used to convert asynchronous behavior back to synchronous behavior. Therefor dzn.async was deprecated in Dezyne 2.16 and removed in Dezyne 2.17. But even if you use Dezyne 2.16 or lower, I advice never to use this feature and in stead use the Async solution from this article.

Using defer

Starting from Dezyne 2.16 the defer keyword has been added. The commands specified in the code block after defer will be executed asynchronously after the current execution finishes. The code block is put in a queue and will be executed in turn. When the state of the component changes, the execution of the supplied code block will be canceled. For more complex cases you can specify which state variables will trigger this. When using defer you have to be mindful about state changes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
component ExampleControlDefer {
    provides ExampleControl api;
    requires Example base;

    behavior {
        bool starting = false;

        on api.Start():
        {
            starting = true;
            Result res = base.Start();
            defer {
                if (res.Ok) api.OnOk();
                else api.OnFail();
                starting = false;
            }
        }

        on api.Cancel():
        {
            starting = false;
            if (base.state.Started) base.Reset();
        }

        on api.Reset():
        {
            base.Reset();
        }
    }
}

A useful feature of the defer is that it can capture variables values as can be seen on line 13. Here the res local variable is captured and evaluated at a later moment when the block is being executed.

/posts/dezyne-async/defer.png

In the simulator we can see when there are actions ready in the defer queue. The defer button will be highlighted. But when looking at the trace as a whole it’s less clear which events were generated by deferred executions. Defer can also not monitor shared state variables, meaning sometimes you will have to introduce a state variable just to be able to cancel a defer. This is also the reason for the starting state variable in the example.

Remake of the Async Interface and Component

I liked the way that dzn.async worked. I am using my own version of it defined as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
interface Async {
    in void req();
    in void clr();

    out void ack();

    behavior {
        bool armed = false;

        on clr: armed = false;
        [!armed] on req: armed = true;
        [armed] on inevitable: { ack; armed = false; }
    }
}

This is used to make synchronous calls asynchronous. It is a limited in that it doesn’t pass along any parameters and it doesn’t have the high priority ack out-event. But it is fully compatible with the use of blocking. By utilizing defer a component that implements this interface can be made. A possible implementation is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
component AsyncComponent {
    provides Async api;

    behavior {
        bool toggle = true;

        on api.req(): defer api.ack();
        on api.clr(): toggle = !toggle;
    }
}

This uses the implicit interface constraints and defer features. When the state of a component changes, outstanding defers are canceled. When calling clr the boolean toggle is toggled, thereby changing the state and cancelling the defer.

We don’t always want to use defer for the implementation. Maybe because we don’t want to use the dzn::pump, we have our own handling of the threading environment or want to run purely single-threaded. Then a foreign component can be used. This makes using the Async interface usable in many different situations.

Using Async Interface

Using the Async interface we can implement our example as follows. We use the same approach as for the ExampleControlDznAsync but now we can also use the implicit interface constraints feature, eliminating the need to track the state in the component.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
component ExampleControlAsync {
    provides ExampleControl api;
    requires Example base;
    requires Async async;

    behavior {
        on api.Start(): async.req();

        on api.Cancel(): async.clr();

        on api.Reset(): base.Reset();

        on async.ack():
        {
            Result res = base.Start();
            if (res.Ok) api.OnOk();
            else api.OnFail();
        }
    }
}

What can be seen in the trace is that the use of the Async interface is very explicit. With this approach you can exactly track the state of the Async interface at any time, which is very helpful for debugging problems in more complex components.

The cancellation of an outstanding req must be done explicitly. The verifier will help to make sure we do not forget to cancel it when needed.

/posts/dezyne-async/async.png

One of the downsides is that we need to provide an implementation of the Async interface. It is best to create a system component that will instantiate the needed helper-components.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
component ExampleControlAsyncComponent {
    provides ExampleControl api;
    requires Example base;

    system {
        ExampleControlAsync impl;
        impl.api <=> api;
        impl.base <=> base;

        AsyncComponent async;
        async.api <=> impl.async;
    }
}

Conclusion

Do not use dzn.async. If you still do, please replace it with the Async alternative described above.

Defer is an easy out of the box solution with some nice properties. But it needs the dzn::pump and can be trickier to use for bigger components.

The Async interface solution is very versatile and explicit, but less flexible. It can be implemented using defer, but for special cases other implementations using foreign components can be made. It is also possible to use it in unittests, because the interface is mockable.