Recent Changes - Search:

edit SideBar

CapeCodeZeroDelayLoops

See Cape Code Host -> Loops for how to avoid zero delay loops in Cape Code.

This page is about the details of the implementation.

The Problem

When the model $PTII/org/terraswarm/accessor/test/auto/WebSocketClientJS.xml:

is run, the following stack trace appears

ptolemy.kernel.util.IllegalActionException: Found a zero delay loop containing .WebSocketClientJS.WebSocketServer
  in .WebSocketClientJS and .WebSocketClientJS.WebSocketServer
        at ptolemy.actor.util.CausalityInterfaceForComposites._computeOutputPortDepth(CausalityInterfaceForComposites.java:755)
        at ptolemy.actor.util.CausalityInterfaceForComposites._computeInputDepth(CausalityInterfaceForComposites.java:694)
        at ptolemy.actor.util.CausalityInterfaceForComposites._computeOutputPortDepth(CausalityInterfaceForComposites.java:763)
        at ptolemy.actor.util.CausalityInterfaceForComposites._computeInputDepth(CausalityInterfaceForComposites.java:694)
        at ptolemy.actor.util.CausalityInterfaceForComposites._computeActorDepth(CausalityInterfaceForComposites.java:530)
        at ptolemy.actor.util.CausalityInterfaceForComposites.checkForCycles(CausalityInterfaceForComposites.java:95)
        at ptolemy.domains.de.kernel.DEDirector.preinitialize(DEDirector.java:1338)
        at ptolemy.actor.CompositeActor.preinitialize(CompositeActor.java:1820)
        at ptolemy.actor.Manager.preinitializeAndResolveTypes(Manager.java:1030)
        at ptolemy.actor.Manager.initialize(Manager.java:702)
        at ptolemy.actor.Manager.execute(Manager.java:354)
        at ptolemy.actor.Manager.run(Manager.java:1252)
        at ptolemy.actor.Manager$PtolemyRunThread.run(Manager.java:1903)

The proposed fix is to set the connection output of accessors/web/net/WebSocketServer.js to be spontaneous:

    this.output('connection', {'spontaneous': true});

However, this does not solve the problem.

Analysis

The MicrostepDelay actor can be used to fix the zero delay loop, see $PTII/org/terraswarm/accessor/test/auto/WebSocketClient.xml. However, we want to create a composite accessor that will run on any accessor host and thus does not use the MicrostepDelay actor.

The MicrostepDelay actor has two methods that are used to fix the loop:

    /** Declare that the output does not depend on the input in a firing.                                        
     *  @exception IllegalActionException If the causality interface                                              
     *  cannot be computed.                                                                                      
     *  @see #getCausalityInterface()                                                                            
     */

    @Override
    public void declareDelayDependency() throws IllegalActionException {
        _declareDelayDependency(input, output, 0.0);
    }
...
    /** Return false indicating that this actor can be fired even if                                              
     *  the inputs are unknown.                                                                                  
     *  @return False.                                                                                            
     */

    @Override
    public boolean isStrict() {
        return false;
    }

commonHost.js defines two methods that deal with spontaneous outputs:

/** Assign priorities to contained accessors based on a topological sort of the                                  
 *  connectivity graph. Priorities are integers (positive or negative), where a lower                            
 *  number indicates a higher priority. The number for an accessor is assured of being                            
 *  higher than the number for any upstream accessor.  An accessor A is upstream of                              
 *  an accessor B if A has an output connected to an input of B and that output is not                            
 *  marked "spontaneous" (that is, it does not have an option with name "spontaneous"                            
 *  and value true). A spontaneous output is produced by an asynchronous callback                                
 *  rather than as a response to an input.  Every directed cycle in a connectivity                                
 *  graph must contain at least one spontaneous output or there will be a deadlock                                
 *  due to a causality loop.                                                                                      
 */

Accessor.prototype.assignPriorities = function() {
...

assignPriorities() calls

/** Assuming that the specified accessor has an assigned priority, follow its                                    
 *  connections downstream and assign priorities to connected accessors.                                          
 *  @param accessor The contained accessor with a priority.                                                      
 *  @param cyclePriority If we encounter an accessor with this priority, then                                    
 *   there is a causality loop.                                                                                  
 */

Accessor.prototype.assignImpliedPrioritiesDownstream = function(accessor, cyclePriority) {
...

and

/** Assuming that the specified accessor has an assigned priority, follow its                                    
 *  connections upstream and assign priorities to connected accessors.                                            
 *  @param accessor The contained accessor with a priority.                                                      
 *  @param cyclePriority If we encounter an accessor with this priority, then                                    
 *   there is a causality loop.                                                                                  
 */

Accessor.prototype.assignImpliedPrioritiesUpstream = function(accessor, cyclePriority) {
    var myPriority = accessor.priority;
    // To get repeatable priorities, iterate over outputs in order.                                              
    for (var i = 0; i < accessor.outputList.length; i++) {
        var output = accessor.outputs[accessor.outputList[i]];
        if (output.spontaneous) {
            // Output is spontaneous, so my priority has no implications                                          
            // for downstream accessors.                                                                          
            continue;
        }
        if (output.destinations) {
            // There are destination accessors.
...

These two functions loop through the outputs and if the output is marked as spontaneous

The priorities are used here:

/** Schedule a reaction of the specified contained accessor.                                                      
 *  This puts the accessor onto the event queue in priority order.                                                
 *  This assumes that priorities are unique to each accessor.                                                    
 *  It is necessary for the react() function to be invoked for this event                                        
 *  to be handled, so this function uses setTimeout(function, time) with time                                    
 *  argument 0 to schedule a reaction. This ensures that the reaction occurs                                      
 *  after the currently executing function has completely executed.                                              
 *  @param accessor The accessor.                                                                                
 */

Accessor.prototype.scheduleEvent = function(accessor) {
...

Solution

The solution was to add declareDelayDependency() to the JavaScript actor. First, we loop through the output ports and add a SingletonParameter to the port if the spontaneous argument is present in the accessor. Then, declareDelayDependency() loops through the ports and declares any ports with spontaneous == true as not depending on any of the inputs.

Edit - History - Print - Recent Changes - Search
Page last modified on May 03, 2016, at 08:45 PM