Multithreaded Object Ownership / Caching Update Problem


#1

Background

I have the following system set up:

  • An ac::Activity is the application logic that does stuff. You can think of it as containing the Controller and Model bits in an MVC design. This runs on the main thread [A].
  • That has a list of vc::Views, which it sends the state of the application. The vc::View is essentially an adaptor for the ac::Activity.
  • The vc::Endpoint is something that runs on a different thread [E], which displays the state however it likes.
  • The vc::_ac::Listener is an input listener that runs on yet another thread [L]. I’m not sure this is the right thing to do, but I want a way for [A] to signal that Views should stop sending input (e.g. when the current Activity is swapped).

Here is a diagram of the relationships between these objects. Colour denotes thread, dashed lines are mpsc::channels, and solid lines are function calls:

With this system, the application can run on its own, and the theory is, whichever view sends it input, it can update all the other views.

Context

Conrod is a GUI library, and I have a Conrod view. What I understand of how it works is, you’ve got a window, and inside that window, you can swap out different Uis to draw different widgets. The Ui caches things in memory. When you want to update the widgets, if you re-use the Ui, drawing will be performant, whereas instantiating a new Ui each time is not performant. Disclaimer: this may be an inaccurate description.

I’ve put the window inside the vc::Endpoint, and the vc::View determines what’s drawn inside it by sending a new Ui to the endpoint whenever the ac::Activity calls show().

Problem

There’s no issue when the activity does not send realtime updates, i.e. the ac::Activity can make a single show() call, and the vc::View just needs to construct the Ui once, and sends it across to the vc::Endpoint transferring ownership. However, when the activity does send realtime updates such as for a loading activity which sends progress updates, the Ui needs to change.

Since the Ui is owned by the vc::Endpoint, and the vc::Endpoint has no knowledge of what’s in the Ui it’s displaying, the logic to update the Ui must live outside the vc::Endpoint, maybe as part of the vc::View or a separate thing. The following options seem to be available:

  1. The vc::View constructs a new Ui object every time (not performant, I’ve tried)
  2. The vc::View wraps the Ui in Arc<Mutex<..>> so it can claim a mutable handle on updates
  3. The vc::View sends a message to the vc::Endpoint and asks for the Ui back, and then re-sends it to the vc::Endpoint after changing it (both sides hold an Option<Ui> or something)
  4. Another separate event loop that the vc::Endpoint invokes, which can update the Ui. The event loop is Activity specific, so it has to be constructed by the vc::View at the same time it creates the Ui (and the vc::View sends a wrapping object that contains the Ui and the event loop).

Are there any better options than the ones above, or do any of the above seem better than the others?