Push node reference

Enable a node to invert the execution path and push data to a parent

Introduction

The goal is to invert the execution path order of two nodes. This allows a child node to push data to a parent node before the parent node OnEvent method is invoked.

Sometimes we want to push data to a shared node for reading by a sibling node. A problem can occur if the read node is not dependent upon the write node. As reader and writer are siblings their order in the execution path is not guaranteed, it is possible for the reader is notified first. This unpredictable behaviour is undesirable and difficult to understand and explain.

One solution is to force the reader to artificially create a dependency upon the writer. We want to move this error prone operation into the framework if possible, as a reader could easily miss this nuanced requirement.

In Fluxtion we use the @PushReference to mark a parent node as an inverted execution dependency. The child OnEvent method will be invoked before the parent OnEvent method.

If the writer specifies the parent node as a push reference then no reader will need to worry about execution order. The push creates an implicit dependency between reader and writer without the reader specifying anything and the processing will behave as expected.

Example

In this example we create a cache as a shared node, with a cache reader and cache writer. The reader and writer are siblings with no explicit dependency. We use the @PushReference annotation in the writer to mark the cache as a push destination.

the code for the example is located here.

The node classes

public class Cache {
    @OnEvent
    public void reconcileCache(){}
}


public class CacheWriter {
    @PushReference
    public Cache cache;
    private final MyEventHandler handler;

    public CacheWriter(MyEventHandler handler) {
        this.handler = handler;
    }

    public CacheWriter(Cache cache, MyEventHandler handler) {
        this.cache = cache;
        this.handler = handler;
    }
    
    @OnEvent
    public void pushToCache(){}
}


public class CacheReader {
    private final Cache cache;
    private final MyEventHandler handler;

    public CacheReader(Cache cache, MyEventHandler handler) {
        this.cache = cache;
        this.handler = handler;
    }

    @OnEvent
    public void readFromCache() {}
}

SEPConfig builder

public class Builder extends SEPConfig {

    @Override
    public void buildConfig() {
        MyEventHandler myHandler = addNode(new MyEventHandler());
        Cache cache = addNode(new Cache());
        CacheReader cacheReader = addNode(new CacheReader(cache, myHandler));
        CacheWriter cacheWriter = addNode(new CacheWriter(cache, myHandler));
    }

}

Generated SEP

The following SEP is generated by Fluxtion. Note that the event processing OnEvent methods is inverted for cache and cache writer. The reader is notified of a change after the cache update, behaving as expected.

public class SampleProcessor implements EventHandler, BatchHandler, Lifecycle {

  //Node declarations
  private final MyEventHandler myEventHandler_1 = new MyEventHandler();
  private final CacheWriter cacheWriter_7 = new CacheWriter(myEventHandler_1);
  private final Cache cache_3 = new Cache();
  private final CacheReader cacheReader_5 = new CacheReader(cache_3, myEventHandler_1);
  //omitted code for clarity

  public void handleEvent(MyEvent typedEvent) {
    //Default, no filter methods
    myEventHandler_1.handleEvent(typedEvent);
    cacheWriter_7.pushToCache();
    cache_3.reconcileCache();
    cacheReader_5.readFromCache();
    //event stack unwind callbacks
    afterEvent();
  }
}

Generated png

The graphical representation of this execution graph as generated by Fluxtion ESC, shows the cache and writer inverted:

Example without @PushReference

As way of illustration we show the generated code and png if the @PushReference annotation is removed from the CacheWriter.

Generated SEP

We can see the generated SEP implements counter-intuitive but correct behaviour as the reader accesses the cache before the writer has updated it.

public class SampleProcessor implements EventHandler, BatchHandler, Lifecycle {

  //Node declarations
  private final Cache cache_3 = new Cache();
  private final MyEventHandler myEventHandler_1 = new MyEventHandler();
  private final CacheReader cacheReader_5 = new CacheReader(cache_3, myEventHandler_1);
  private final CacheWriter cacheWriter_7 = new CacheWriter(myEventHandler_1);
  //Dirty flags

  public void handleEvent(MyEvent typedEvent) {
    //Default, no filter methods
    myEventHandler_1.handleEvent(typedEvent);
    cacheReader_5.readFromCache();
    cacheWriter_7.pushToCache();
    //event stack unwind callbacks
    afterEvent();
  }
  //code omitted for clarity
}

Generated png

The image shows the CacheReader and CacheWriter are siblings with no explicit priority order. Also note because the Cache has no event handler parent nodes it is not included in any execution paths. Absence from an execution path means the reconcileCache() method is never called.

Last updated