ParaView Client/Server communication and abort support

I’m currently working on supporting abort in ParaView and in that context I had to understand the client/server communication protocol, which is virtually undocumented even in developer docs.

So I decided to share my findings here and also share how I will actually adress aborting a filter in client/server mode.

Please note this is not the same as supporting asynchronous UI, which is another thing entirely, but is a step in that direction.

What happens when you press apply in client/server mode and there is no progress events:


        CLIENT                                       SERVER               
          β”‚                                            β”Œβ”€β”                
          β”‚                                            β”‚β”‚β”‚                
          β”‚                                            β”‚β”‚β”‚Receive()       
         β”Œβ”΄β”  ExecuteStream("UpdatePipeline")          β”‚β”‚β”‚                
   Send()β”‚β”œβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ””β”€β”˜                
         β””β”¬β”˜                                           β”Œβ”€β”                
         β”Œβ”΄β”                                           β”‚β”‚β”‚                
   Send()β”‚β”œβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚β”‚β”‚                
         β””β”¬β”˜                       β”‚                   β”‚β”‚β”‚                
         β”Œβ”€β”                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚UpdatePipeline()
         β”‚β”‚β”‚  ExecuteStream("CleanupPendingProgress")  β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
         β”‚β”‚β”‚                       β”‚                   β””β”€β”˜                
         β”‚β”‚β”‚                       └───────────────────►│                 
         β”‚β”‚β”‚                                           β”Œβ”€β”                
         β”‚β”‚β”‚                                           β”‚β”‚β”‚Cleanup()       
         β”‚β”‚β”‚                                           β””β”€β”˜                
Receive()β”‚β”‚β”‚       CLEANUP_TAG                         β”Œβ”€β”                
         │││◄──────────────────────────────────────────┼─│Send()          
         β””β”€β”˜                                           β””β”€β”˜                

As you can see, while the server is actually processing the data and doing the computation, the client is already trying to receive the CLEANUP_TAG, and waits in that state until the server is sending it.

With ProgressEvent in between, here is how it looks:

                           CLIENT                                       SERVER               
                             β”‚                                            β”Œβ”€β”                
                             β”‚                                            β”‚β”‚β”‚                
                             β”‚                                            β”‚β”‚β”‚Receive()       
                            β”Œβ”΄β”  ExecuteStream("UpdatePipeline")          β”‚β”‚β”‚                
                      Send()β”‚β”œβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ””β”€β”˜                
                            β””β”¬β”˜                                           β”Œβ”€β”                
                            β”Œβ”΄β”                                           β”‚β”‚β”‚                
                      Send()β”‚β”œβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚β”‚β”‚                
                            β””β”¬β”˜                       β”‚                   β”‚β”‚β”‚                
                            β”Œβ”€β”                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚UpdatePipeline()
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                   Receive()β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
   OnWrongTagEvent          β”‚β”‚β”‚            PROGRESS_TAG 0.33              β”‚β”‚β”‚                
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β—„β”€β”€β”‚β”˜β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”‚                
β”‚ RefreshProgress(0.33) β”‚   β”‚ β”‚                       β”‚                   β”‚β”‚β”‚                
└───────────────────────┴───►┐│                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
   OnWrongTagEvent Receive()β”‚β”‚β”‚            PROGRESS_TAG 0.66              β”‚β”‚β”‚                
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β—„β”€β”€β”‚β”˜β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”‚                
β”‚ RefreshProgress(0.66) β”‚   β”‚ β”‚                       β”‚                   β”‚β”‚β”‚                
└───────────────────────┴───►┐│                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚   ExecuteStream("CleanupPendingProgress") β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β”‚β”‚β”‚                
                            β”‚β”‚β”‚                       β”‚                   β””β”€β”˜                
                            β”‚β”‚β”‚                       └───────────────────►│                 
                            β”‚β”‚β”‚                                           β”Œβ”€β”                
                            β”‚β”‚β”‚                                           β”‚β”‚β”‚Cleanup()       
                            β”‚β”‚β”‚                                           β””β”€β”˜                
                   Receive()β”‚β”‚β”‚       CLEANUP_TAG                         β”Œβ”€β”                
                            │││◄──────────────────────────────────────────┼─│Send()          
                            β””β”€β”˜                                           β””β”€β”˜                

As you can see, the magic here is the OnWrongTagEvent Logic that let CLIENT receive the wrong message and execute code in reaction, before resuming the receive wait.

Given all of that, how do you fit a Abort logic in there.

For starters, we must stop the client for starting the cleanup progress stuff right away but instead wait for the completion of the pipeline before the cleanup.

Second we need a way for the client to inform the server that Abort was triggered.
Adding a bidrectionnel OnWrongTagEvent may be possible but at this point an actual event queue would be prefferable and that is way too complex to set that up.

However, I can simply flag a proxy as aborted on client side and when processing a progress event, informing the server that a specific proxy have been aborted.

Here is how it looks:

                                                  CLIENT                                       SERVER               
                                                    β”‚                                            β”Œβ”€β”                
                                                    β”‚                                            β”‚β”‚β”‚                
                                                    β”‚                                            β”‚β”‚β”‚Receive()       
                                                   β”Œβ”΄β”  ExecuteStream("UpdatePipeline")          β”‚β”‚β”‚                
                                             Send()β”‚β”œβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ””β”€β”˜                
                                                   β””β”€β”˜                                           β”Œβ”¬β”                
                                                   β”Œβ”€β”                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚UpdatePipeline()
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                          Receive()β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                          OnWrongTagEvent          β”‚β”‚β”‚            PROGRESS_T   0.33              β”‚β”‚β”‚                
                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β—„β”€β”€β”‚β”˜β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”‚                
                       β”‚ RefreshProgress(0.33) β”‚   β”‚ β”‚            ABORT FALSE                    β”‚β”‚β”‚                
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β–Ίβ”Œβ”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                          OnWrongTagEvent Receive()β”‚β”‚β”‚            PROGRESS_T   0.66              β”‚β”‚β”‚                
                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β—„β”€β”€β”‚β”˜β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”‚                
                       β”‚ RefreshProgress(0.66) β”‚   β”‚ β”‚                                           β”‚β”‚β”‚                
TriggerAbort()────────►│ Abort = true;         β”‚   β”‚ β”‚             ABORT TRUE                    β”‚β”‚β”‚                
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β–Ίβ”Œβ”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚β”‚β”‚                
                                                   β”‚β”‚β”‚                                           β””β”€β”˜                
                                          Receive()β”‚β”‚β”‚       STREAM_EXECUTED_TAG                 β”Œβ”¬β”                
                                                   │││◄──────────────────────────────────────────┼─│Send()          
                                                   β””β”΄β”˜                                           β””β”΄β”˜                
                                                   β”Œβ”¬β”   ExecuteStream("CleanupPendingProgress")  β”‚                 
                                                   β”‚β”œβ”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”Œβ”Όβ”                
                                             Send()β”‚β”‚β”‚                                           β”‚β”‚β”‚                
                                                   β””β”΄β”˜                                           β”‚β”‚β”‚Cleanup()       
                                                   β”Œβ”¬β”                                           β””β”€β”˜                
                                                   β”‚β”‚β”‚       CLEANUP_TAG                         β”Œβ”€β”                
                                          Receive()│││◄──────────────────────────────────────────┼─│Send()          
                                                   β”‚β”‚β”‚                                           β””β”€β”˜                
                                                   β””β”€β”˜                                                              

By waiting for Receive(STREAM_EXECUTED_TAG), I ensure the socket stays free of clutter and can be used by the client to send a ABORT information back.

The triggerAbort() is a bit tricky and can be done thanks to Qt letting us input event during the RefreshProgress call, but improving this is a whole other subject.

I do not look specifcally for feedback here, but if you have any, feel free!

1 Like