In the tutorial on the usage of the ‘async’ keyword, we already reviewed a situation where we did not fully trust a native component and called that an armour.
In this tutorial we will generalize this concept of armouring:
An Armour protects a system built in Dezyne during runtime against interface violations by native code.
Between Dezyne components such violations can never occur after passing the verification.
However, the integration with native code cannot be formally verified and hence the native code could be misbehaving.
Typically if the native code is not fully understood it may be suspected of not being fully interface compliant.
An Armour component shields the Dezyne system by capturing misbehavior and ensuring that the Dezyne components only perceive good behaviour.
We call the interface between the Armour and the Dezyne component the Strict interface.
We call the interface between the armour and the Native code the Robust interface.
The Robust interface does not enforce the restrictions of the Strict interface and so, in a sense, allows misbehaviour.
Depending on the situation the Armour could simply correct the violations such that the Dezyne components never notice anything.
The Armour could also raise an error condition which must be handled by the Dezyne components first before normal behaviour is continued.
The Armour examples provided in this tutorial all come in two flavours representing the situation with and without error handling.
In all cases the error is logged for diagnostics purposes.
The list is exhaustive in the sense that for any single possible interface violation an example is provided.
The list may appear surprisingly small but many problems are static in nature and are detected by the compiler.
Off course in practice multiple violations to an interface may occur and then multiple Armour patterns would need to be combined.
As an example of the situation without error handling look at the often used example of a device that is always switched on and off in alternation.
The interface would specify that it is illegal to send an on-command if it is already on and similarly illegal to send an off -command if it is already off.
In the implementation there is not a real problem to accept an on-command if it already on so that is not the reason to be strict in the interface.
However, if we want to verify whether this device is used in a proper alternating way then having these strict interfaces
supports such a verification.
In this situation an Armour would need to log the error situation but probably the system could simply continue to execute.
In more complex situations an Armour cannot solve a violation independently.
Since the behaviour is unexpected by the Dezyne components appropriate handling is not available and a general error status would need to be raised in the Dezyne components.
Also the native component generating the erroneous event must be reset to a known situation.
This means a generic pattern of error status handling must be present in the Dezyne components to be prepared for interaction with Armour components.
In the Armour patterns provided we assume that the Dezyne component directly communicating with the Armour takes full responsibility for this error handling and hides this completely for other components making use of the functionality.
This may not be applicable in all situations and then the error should be passed on.
This is just one example how such error handling is situation dependent.
This means that many different patterns of error handling are possible; in this tutorial and the given examples we consistently apply the pattern described below.