Multithreaded Object Ownership / Caching Update Problem

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:

Thread Relationships

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?