Context reminder: a “process” or “controller” class (call it as you want) implements quite heavy app logic, execution of which can last many seconds during which we want to inform the user on progress with suitable graphical updates.
What I tried (and understood - if I’m right):
(a) From GUI to the Controller: main GUI components use sendActionMessages (they implement ActionBroadcaster) and the controller implements ActionListener. By comparison the changeBroadcaster would only propagate a generic change event and coalescing applies. Based on the action that is posted, the controller may enter a long lasting processing job. Fact is, action messages are handled asynchronously by the main message thread. When this one decides to invoke the controller’s actionListenerCallback(msg) it does it on the main message thread.
(b.1.) from the Controller to GUI: I have added public methods on various GUI Components that invoke sub-component GUI updates to reflect Controller’s progress. The Controller can invoke these methods, and all updates gets queued (if I am right…). My Controller being busy on the main message thread, all updates it trigerred are not displayed until the Controller returns from actionListenerCallback(msg) [my observation].
(b.2 - alternate tentative) I wrapped my long lasting process into an AsyncUpdate callback, letting actionListenerCallback(msg) return immediately. But again, when handleAsyncUpdate() is executed, that’s under the main message thread and same outcome as (b.1)
(b.3) I fork a Thread from actionListenerCallback(msg) to let the callback return immediately, and the thread executes my long lasting logic but can no longer invoke the methods I exposed that perform GUI component updates because only the main message thread can do. I thought about calling indirectly through AsyncUpdater (now in the direction Controller - > GUI compo), wrapping the GUI update methods inside handleAsyncUpdate() callbacks… that shall work indeed, but I cannot convey any arguments through triggerAsynchUpdate() to pass data for these updates. I can think too about creating a reverse ActionBroadcaster/Listner channel… shall work too, but again the code will get bloated by serializing/deserializing data for updates (fan-IN through sendActionMessage(msg) + Fan-out from actionListenerCallback(msg) )
(b.4) finally, still forking a thread for the long running Controller tasks and just adding one single line of code:
const MessageManagerLock mmlck;
at the start of all methods invoked by my foreign thread (those methods exposed by top GUI components/containers and that perform GUI updates) was simple and elegant. And I can’t forsee any nasty effect of doing that. Locks are very short runs and only invoke basic text, graphic, and button updates. My app now starts the long running task in backgound an I can follow its progress in real time [my observation]
Note that in a “normal” app, all Controller-side tasks (also case for most other actions performed by my controller) are quite fast (far below 1 sec) and so there’s no need to fork a thread and lock the message loop time to post “intermediate” GUI updates. All updates even only visibly taking place after actionListenerCallback(msg) returns become instantaneous for the end user.
So far so nice.