Home Demos Forum Research Guide
Viewing topic in forum 'announcements'
back to topic list

Dana v163
Dana v163 provides bug fixes, performance enhancements, and a heavily revised runtime adaptation protocol. Here we discuss that protocol in detail.

Runtime adaptation

Previous versions of Dana used a relatively inflexible runtime adaptation system that was able to rewire required-provided interface connections, transferring state via the clone() procedure.

This system had several problems. Firstly it used the same procedure to adapt every kind of component. This meant stopping interaction with the current component by pausing a required interface and waiting for all interaction through it to stop, copying state to the new component, and waiting for the old component to be destroyed before allowing calls to proceed into the new component. For components that could take a long time to be destroyed, or for components that had long-running function calls that would impede the progress of the pause stage, this could mean long delays in completing an adaptation. Although nothing would be "lost" during these delays, the effect would be apparent jitter in the application as function calls were held at interfaces.

Secondly, the use of "clone" for state transfer meant that the programmer had to expose public functions that allowed access to all necessary state. This meant that, in some cases, there was no choice but to allow some otherwise "private" information to be publicly accessed. In addition to this there was no feature to differentiate an adaptation-driven "clone" from one that was being done to copy an object outside of the adaptation process, leading to defensive clone procedures.

And finally, the existing procedure only worked for objects that were instantiated via a required interface. For objects that were dynamically bound using the "from" syntax, no adaptation was possible.

v163 includes a major reworking of the runtime adaptation system that addresses all of the above. The procedure is illustrated below:


void adapt(IDC ofComponent, char type[], IDC toComponent) {
   if (ofComponent :> type != null) {
      //prevent new objects being created or destroyed
      dana.pause(ofComponent :> type)
      //switch the binding to the new component
      ofComponent :> type = toComponent :< type
      Object objects[] = toArray(dana.getObjects(ofComponent :> type))
      dana.resume(ofComponent :> type)
      //switch all existing objects to the new class
      for (int i = 0; i < objects.arrayLength; i++) {
         if (dana.pauseObject(objects[i])) {
            // - wait for all in-progress calls to finish
            dana.waitForObject(objects[i])
            // - create a new object, state-linked to the current one
            Object a = dana.adaptConstruct(source :< type, objects[i])
            // - rewire object so calls now go to the new one
            Object b = dana.rewireObject(objects[i], a)
            // - allow new calls to proceed in the new object
            dana.resumeObject(object[i])
            // - destroy the old object
            dana.adaptDestroy(b)
         }
      }
   }
}
 


This procedure is modifiable to accommodate stateful and stateless components, providing the highest possible adaptation speed in every situation, it introduces special "transfer" state that avoids the use of the clone() function for adaptation purposes, and it can be used both with required interfaces and with individual objects.

In the above, we first pause the required interface very briefly to acquire the list of objects that has been instantiated over this interface. We then rewire and resume the required interface almost immediately. This allows new object instances to be created, using the new component.

We then look at the list of object instances sourced from the previous component and we need to transfer each of these instances to the new component. We therefore iterate through each one and perform some transfer logic.

In the above version of the procedure we assume stateful objects. We therefore wait for all in-progress calls to finish (ensuring no further externally-driven state transitions) before instantiating the new object and then destroying the old one.

For stateless objects, however, we can take a shortcut, using the following code for the object transfer part:


if (dana.pauseObject(objects[i]))
   {
   Object a = dana.adaptConstruct(source :< type, objects[i])
   Object b = dana.rewireObject(objects[i], a)
   dana.resumeObject(objects[i])
   dana.waitForObject(b)
   dana.adaptDestroy(b)
   }
 


There are further possible orderings of these operations that achieve different effects; the entire procedure is therefore far more configurable than before.

When adapting a specific object (i.e. one that was dynamically bound using the "from" syntax) we can ignore the part of the logic above that relates to the required interface, and simply pass the specific object we want to adapt to the inner part of the adaptation procedure.

For convenience we've wrapped all of this logic up in a new utility component, composition.Adapter, the interface of which has functions for adapting required interfaces as well as individual objects.

A new variable scope and qualifier: transfer

Along with the new adaptation model discussed above, we've introduced a new way of transferring state between adapted objects. This is done by allowing variables to be declared on interfaces, annotated with the "transfer" qualifier:


    interface Counter{
       transfer int number
       int getNext()
       }
 


These variables work like instance globals within the implementation, but are shared between different implementations of the object during adaptation. There is therefore no need for objects sourced from a new implementation to use function calls on the old implementation to extract state during adaptation; instead this state is immediately available via the transfer fields of the interface. For the purposes of generalised adaptation, note that Dana considers interfaces with no transfer fields to the "stateless", and those with one or more transfer fields to be "stateful". Finally, transfer fields are inherited in interface subtypes.

Together we believe that these new mechanics provide a large range of new possibilities in adaptive systems written in Dana, and we're excited to see what we can do next!

Happy coding :-)
by barry on 2016-02-28 18:21:35 (0 replies)

© Francis Research Ltd. 2018