A snappy PothosGUI and the new EvalEngine Wed, Nov 12 2014 AM
Making a static application that initializes objects, connections objects, and applies settings is simple and predictable. Hopefully if something goes wrong, there is a helpful error, you fix it, and then restart the application.
However, the GUI is an entirely different beast. We are managing a set of blocks, remote processes, and a topology from within the GUI. At any point, a block could segfault, a server could go offline, or the user could apply a terrible setting to a block. The GUI cant crash, it has to recover gracefully, and display the error in a useful way.
First attempt
So to get things moving, the GUI was a little half-assed in terms of dealing with such a dynamic environment. For one, there was no background evaluation. So starting a topology, making changes while the topology was running, or applying new settings on a block had a noticeable lag. Also, we sucked at error recovery and error notification. If a server went down, or the remote process crashed, it was going to stay down forever until the GUI is restarted.
The new evaluation engine
I spent the last few days re-writing the evaluation code to be completely asynchronous. That means, all state changes from the GUI are relayed into the evaluator without any return values. And the evaluator relays the latest evaluation state back to the graph blocks and graph editor upon completion. Imagine this as a queue of events into the evaluator and a queue of response events out of the evaluator. Fortunately, threading, queuing, and asynchronous events are all standard QObject capabilities.
The evaluation engine was merged today into master.
- There is no longer any delay or lag for typing junk into a parameter entry box.
- Making changes to the topology or reverting to a previous state in instantaneous.
- Errors like process crashed and remote host offline are presented to the block.
- There are graphical hooks to force the re-evaluation of selected blocks.
Now in the background, the evaluator thread is trying to create the most up-to-date version of the design. The thread is either currently working on evaluating a state or waiting for a new event. We throw out all intermediate states that the GUI thread enqueued while the evaluator was working. This is known as event compression. As a side note, the Pothos Framework worker actors actually use event compression as well by never entering the work() routine while there are enqueued messages.
Eval engine work continues...
What is merged into the master now is far-better than its predecessor. But, the current evaluation engine is by no means perfect. It could be more precise about reacting to changes. The evaluator is doing more work per event than it strictly has to, really at the expense of making the code more tedious to implement. The real issue is that problems like this are better expressed as state machines. The problem can be flattened out into events, calls, and conditions. Then the state machine would be expressed in a high level description which could be parsed and executed at runtime.