Previous: Component reply
, Up: Component Imperative Statements [Contents][Index]
defer
defer
is a keyword that may be placed in front of an imperative
statement.
defer-statement ::= "defer" argument-list? imperative-statement argument-list ::= "(" ")" | "(" expression ("," expression)* ")"
defer
indicates that the execution of the corresponding statement
must be postponed at least until after returning back to the
caller. Note that in order for the deferred statement to execute, the
surrounding system must have reached an overall state where it can
accept new activating events, i.e., this state is a system wide run to
completion state.
The primary goal of defer
is to decouple the execution of an
imperative statement from the caller. This allows implementing an
asynchronous interface almost as concisely as implementing it
synchronously, as demonstrated by the example below.
interface ihelloworld { in void hello (); out void world (); behavior { bool idle = true; on hello: world; [idle] on hello: idle = false; [!idle] on inevitable: { idle = true; world; } } } component synchronous_asynchronous { provides ihelloworld h; behavior { bool synchronous = false; [synchronous] on h.hello (): h.world (); [!synchronous] on h.hello (): { synchronous = true; defer { synchronous = false; h.world (); } } } }
Here we can observe the difference between synchronous and asynchronous
behavior once more. When the synchronous
boolean equals
true
the world
action occurs in the context, e.g. between
the hello
and its return
. When synchronous
equals
false
the world
action occurs after the return
.
This behavior is clearly depicted by the following state diagram.
Perhaps the idle
state might seem superfluous in the example
above, however it is not. Besides resulting in component behavior which
is not compliant with its interface, removing the idle
state and
the corresponding guard would allow a client to do multiple consecutive
h.hello
’s, which results in an overflow of the defer queue.
Besides state playing a role in avoiding defer
queue overflow,
there is another aspect related to state and the use of defer
.
In order for the deferred statement to execute, the component must
remain in the same state as it was at the time of invoking defer
.
Anything that changes the state of the component after invoking
defer
but before the deferred statement executes will remove it
from the queue, and thereby implicitly cancel it. This is demonstrated
by the example below. Note that a data member variable is not part of
the component state, and changing its value does not cancel the deferred
statement.
interface ihellocruelworld { in void hello (); in void cruel (); out void world (); behavior { bool idle = true; [idle] on hello: idle = false; [!idle] on inevitable: { idle = true; world; } on cruel: idle = true; } } component defer_cancel { provides ihellocruelworld h; behavior { bool idle = true; [idle] on h.hello (): { idle = false; defer { idle = true; h.world (); } } on h.cruel (): idle = true; } }
Here we see that the cruel
event makes the component idle
again and in compliance with the interface this implies that the
world
event can no longer occur. The corresponding component
state diagram is depicted below.
The ability to cancel a deferred statement is not always desirable. The
way to influence the skip behavior is to add an argument list of state
variables to the defer
keyword. This limits the scope of the
state which is observed by defer
in deciding when to skip the
execution. The two extreme cases are:
defer
without
an arument list.
We can see an example of a defer
argument list below.
interface ihelloworld { in void hello (); out void world (); behavior { bool idle = true; on hello: world; [idle] on hello: idle = false; [!idle] on inevitable: { idle = true; world; } } } interface icruel { in void cruel(); behavior { on cruel: {} } } component defer_selection { provides ihelloworld h; provides icruel c; behavior { bool synchronous = false; bool cruel = false; on c.cruel(): cruel = !cruel; [synchronous] on h.hello (): h.world (); [!synchronous] on h.hello (): { synchronous = true; defer(synchronous) { synchronous = false; h.world (); } } } }
Here the execution of the deferred statement must remain unaffected by
the change to the cruel
state variable. We can achieve this by
only observing the state
variable as the example shows or not
observing any state at all. The latter case is left as an exercise to
the reader.
Previous: Component reply
, Up: Component Imperative Statements [Contents][Index]