tn 1 Asynchronous and Synchronous Creation States - modelint/shlaer-mellor-metamodel GitHub Wiki
mint.sm-meta.state.tn.1 / Leon Starr / Version 0.4.0 / 22-4-9
Comments, questions, and other feedback welcome in the discussions area.
This technical note is motivated by a proposal submitted to the Shlaer-Mellor Commons. As originally written, it reads as follows:
The creation state has always been a bit anomalous. The behaviour of the creation state entry action is class-based (since no instance exists at this point) whereas all other states have instance-based entry actions. With the addition of synchronous behaviour on classes (functions, operations) a creation state is no longer necessary. Creation occurs in a synchronous operation (where identifier, appropriate attributes and initial state can be set). The newly created instance is then ready to receive events directed to that instance and all the states of the state model have instance-based behaviour. The initial state could have an entry action associated with it but that action is not executed on creation. If the state model ever returns to that state then the entry action would be executed. More than one state of the state model could be used as the initial state in a creation operation. By extension creation events are no longer required either.
Before diving in, let's clarify terms and elements of the instance creation process. Here and there I will replace some of the ancient scroll terminology with more recent UML terms.
Shlaer-Mellor used the term 'object' in OOA96. This term may lead to varied interpretation when considering real world entities modeled as one or more levels of specialization. Here I am using the term instance to refer to an instance of one, and only one class. If you consider the table view of a populated class, the term instance refers to a row in that table.
Shlaer-Mellor (SM) specifies two ways to create an instance in both [OOA96] and [MB]: asynchronous and synchronous. SM also introduced the corresponding terms self created and non-self created. While these last two terms never gained any traction as far as I can tell, they may say more about the analysis distinction we really need to make. In fact, I'm going to go a bit further and propose the term delegated creation as an alternative to self creation to keep the perspective on the initiator.
And by initiator, I mean a state activity of a lifecycle (instance based) state machine. OOA96 indicates that this restriction is intended to prevent assigner state machines from being able to create non-associative instances. While I agree with this specific restriction, I am not sure that the authors of OOA96 considered that the rule limiting creation initiation to a lifecycle state machine also prevents class methods, domain operations and ee operations from creating instances. But that's a different discussion that we can sidestep for now.
And by state activity I am referring to the set of zero or more actions executed upon entry to a state. Every state defines an activity, but not every activity defines actions.
Both types of creation rely on some variation of the create accessor defined in chapter 9 of OOA96. A create accessor initializes all attributes of a newly created instance and may or may not set the initial state of that instance (depending on which variation is employed).
Synchronous creation definitions
With synchronous creation (non-self creation), the create accessor is invoked within an activity in the initiator lifecycle. I'm going to refer to the synchronous create action as the action that invokes any variation of the create accessor directly. This action is synchronous because the target instance is guaranteed to be created upon completion of the action. In fact, the action returns a reference to the newly created instance. This means that the synchronous creation action must block until creation is complete. Assuming no data dependencies, other actions within the same activity may run in parallel and possibly complete while the creation action is blocked.
Asynchronous creation definitions
As defined in OOA96 and MB, asynchronous creation (self creation) requires a creation event and a creation state. According to OOA96, The creation event 'causes' execution of a creation state's activity. Also, according to OOA96, the activity of a creation state must contain one or more actions that collectively apply the create accessor. OOA96 provides no such example, but MB features an example of a creation event/state combination as excerpted below:
images/state-tn1/5-mb-creation-state.png
In the above example we see a number of actions initializing the values of the newly created instance's attributes. This includes the initialization of any referential attributes and, hence, required references from the newly created instance. Taken together, these actions constitute a complete create accessor as defined in OOA96. Well, except for the actual instance creation which is implied by the creation state itself.
Note the caption for Figure 10.6 at the bottom of the excerpt: "New Shipment is Created, Enters its Initial State, and Executes its Procedure".
Wait. How is this ordering possible? It seems to imply that the Shipment instance exists before the procedure (activity) that initializes the attribute values is executed. This not only breaks our relational interpretation of what it means to be an instance (a row/tuple), it also implies that the instance exists prior to invocation of the OOA96 create accessor. We know this isn't what actually happens, so the whole mechanism here is a bit misleading. This inconsistency supports the proposal to deprecate the creation state.
As indicated in this proposal, both the creation state and creation event have anomalous properties.
The creation state is an ordinary state in the sense that its activity is executed and, upon completion, the newly created instance remains in that state waiting to process any incoming non-creation event. It is anomalous in that the instance does not exist until the creation state activity has been completed. The state also has the unique property that it may not be re-entered by any other transition for reasons made clear in OOA96. In fact, a creation state may only be entered by a special transition which has no source state. When we use UML notation, we make use of the initial pseudo-state transition to illustrate this source-less transition into the creation state. Also, the two create accessor variations defined in OOA96 that specify the state in which the new instance is created may not be invoked by a creation state activity as the state is already determined.
The creation event is even more peculiar.
It behaves like a normal (non-creation) event since it also triggers a transition into a state. Unlike a non-creation event, however, it may only trigger a transition into a creation state. Consequently, a creation event may only be associated with an initial pseudo-state transition. Also, the ignore and can't happen responses do not apply to creation events, a creation event is not addressed to an instance, a creation event may not be addressed to an assigner state machine, and finally, a creation event must specify the name of a class with a modeled lifecycle.
All that said, the desire to deprecate both creation states and events is understandable. In doing so, however, we effectively deprecate asynchronous creation. Are we then throwing out the baby with the bath water? Wahhh!
In defense of asynchronous creation
Setting aside the peculiarities of creation events and states, let's consider the key properties of the two types of creation.
With synchronous creation, the initiating activity must block until the new instance is created. There are benefits and disadvantages to this fact. The clear benefit is that the initiating state machine knows with certainty that the instance it created exists upon completion of the creation action. In fact, the action returns a reference to the newly created instance. On the downside, the initiating activity cannot exit and proceed with any other tasks until creation is complete. Time and synchronization play out differently in each case. In many cases, the specified system will be similar enough in behavior that the difference won't matter much. In some target platforms though, the results can be greatly magnified depending on how many instances are created at once, and how long it takes to create an individual instance.
Another thing to consider is the whole object-oriented encapsulation idea. When a modeler says that an instance is created asynchronously, the modeler is saying that the initiator is delegating creation of the instance. Elimination of asynchronous creation effectively requires that the initiator get wrapped up in the mechanics of creating an instance that is better suited to create itself. The resultant model has its encapsulation muddied as a consequence.
Changing the way we delegate self-creation
Before jumping into our examples, let's rethink how we can specify asynchronous creation.
The Model Integration/Realization approach defines an asynchronous creation action that consists of an instance directed event and an attribute initialization specification that the MX (Model Execution) domain can use to instantiate the target class. Rather than have the modeler explicitly assign initial attribute values, we can let the MX domain invoke the OOA96 create accessor using the information supplied in the asynchronous create action. There is no work left over for a creation state to perform, so we can simply enter a normal (non-creation) state with the supplied initial event.
If you want to know more about how the referential attributes can be initialized, see Andrew Mangogna's [Relops] paper.
Here we see the MB example recast using this scheme:
images/state-tn1/50-mir-mb-asynch-style.png
Now let's take a look at a couple of examples where we can employ this mechanism in situations where asynchronous creation may be preferrable.
Example 1: Creation requires extensive computation
The example below has been contrived to suit an application where the new instances require a bit of computation. A single instance of Simulation Environment
wants to create, let's say ten instances of Agent
.
images/state-tn1/30-asynch-example.png
As an aside, we specified the quantity of desired instances as part of the creation action to simplify the activity. No point writing the same action ten times, especially when the total quantity could change at runtime.
While those Agent
instances are busily creating themselves, we have some computation of our own to do. Why wait?
Once our Simulation Environment
has entered the READY FOR AGENTS
state, it can check to see if all of the requested Agent
instances have been created. If not,
it waits.
Keep in mind we could have multiple instances of Simulation Environment
each creating their own Agent
set.
Example 2: Creating many distributed instances
This next example is a variation on this theme, but this time we are creating a lot more instances. Also we put the focus on the creation process itself as opposed to any additional initialization actions.
images/state-tn1/40-asynch-example.png
In this example we tell one million Things
to create themselves. Our source state machine need not synch with those Things
. It just moves on with its lifecycle, while the creation is in progress, and then eventually deletes itself.
When we are implementing on a single CPU the creation process can be fast and predictable. But when creation is on a distributed or massively parallel platform network latencies and other factors may influence the creation process such that we don't want the initiating state machine to lock up while a process that is both potentially time consuming and time variant.
The models are platform independent so they specify the same behavior in either case.
Final thoughts: delegated creation
Can you refactor all of your models to use only synchronous creation and still get them to specify the same overall behavior? Maybe. But will those same models be as resilient, expressive, and manageable?
Earlier in this note I proposed the term 'delegated creation'. Asynchronous creation provides the modeler with a way to initiate creation of one or more instances without having to incorporate the actual creation process into the initiator's lifecycle. This concept is a fundamental aspect of OOA and the whole object oriented paradigm where each class is responsible for its own lifecycle, coordinating as necessary with other classes. If we refactor all of our models so that creation is only synchronous, I fear that the paradigm would be compromised in favor of a more intrusive, controller-like collaboration among classes.