39

I have seen, in many places, that it is canonical wisdom1 that it is the responsibility of the caller to ensure you are on the UI thread when updating UI components (specifically, in Java Swing, that you are on the Event Dispatch Thread).

Why is this so? The Event Dispatch Thread is a concern of the view in MVC / MVP / MVVM; to handle it anywhere but the view creates a tight coupling between the view's implementation, and the threading model of that view's implementation.

Specifically, let's say I have an MVC architected application that uses Swing. If the caller is responsible for updating components on the Event Dispatch Thread, then if I try to swap out my Swing View implementation for a JavaFX implementation, I must change all the Presenter / Controller code to use the JavaFX Application thread instead.

So, I suppose I have two questions:

  1. Why is it the caller's responsibility to ensure UI component thread safety? Where is the flaw in my reasoning above?
  2. How can I design my application to have loose coupling of these thread safety concerns, yet still be appropriately thread-safe?

Let me add some MCVE Java code to illustratrate what I mean by "caller responsible" (there are some other good practices here that I'm not doing but I'm trying on purpose to be as minimal as possible):

Caller being responsible:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        view.setData(data);
      }
    });
  }
}
public class View {
  void setData(Data data) {
    component.setText(data.getMessage());
  }
}

View being responsible:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    view.setData(data);
  }
}
public class View {
  void setData(Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        component.setText(data.getMessage());
      }
    });
  }
}

1: The author of that post has the highest tag score in Swing on Stack Overflow. He says this all over the place and I have also seen it being the caller's responsibility in other places, too.

11
  • 1
    Eh, performance IMHO. Those event postings don't come for free, and in non-trivial apps you'd want to minimize their number (and make sure none are too large), but that minimization/condensation should logically be done in the presenter.
    – Ordous
    Sep 2, 2015 at 19:21
  • 1
    @Ordous You can still ensure that the postings are minimal while putting the thread handoff in the view.
    – durron597
    Sep 2, 2015 at 19:23
  • 3
    I read a really good blog some time ago which discuss this issue, what it basically says is that it is very dangerous to try and make the UI kit thread safe, as it introduces possible deadlocks and depending on how it's implemented, race conditions into the framework. There is also a performance consideration. Not so much now, but when Swing was first released it was heavily criticised for it's performance (been bad), but that wasn't really Swing's fault, it was people's lack of knowledge with how to use it. Sep 2, 2015 at 22:11
  • 1
    SWT enforces the concept of thread safety by throwing exceptions if your violate it, not pretty, but at least you're made aware of it. You mention changing from Swing to JavaFX, but you'd have this problem with just about any UI framework, Swing just seems to be the one which highlights the problem. You could design a intermediate layer (a controller of a controller?) whose job it is to ensure that calls to the UI are synchronised correctly. It's impossible to know exactly how you might design your non-UI parts of your API from the perspective of the UI API Sep 2, 2015 at 22:14
  • 1
    and most developers would complain that any thread protection implemented into the UI API was to restrictive or didn't meet their needs. Better to allow you to decide how you want to solve this issue, based on your needs Sep 2, 2015 at 22:15

5 Answers 5

24

Toward the end of his failed dream essay, Graham Hamilton (a major Java architect) mentions if developers "are to preserve the equivalence with an event queue model, they will need to follow various non-obvious rules," and having a visible and explicit event queue model "seems to help people to more reliably follow the model and thus construct GUI programs that work reliably."

In other words, if you try to put a multithreaded facade on top of an event queue model, the abstraction will occasionally leak in non-obvious ways that are extremely difficult to debug. It seems like it will work on paper, but ends up falling apart in production.

Adding small wrappers around single components probably isn't going to be problematic, like to update a progress bar from a worker thread. If you try to do something more complex that requires multiple locks, it starts getting really difficult to reason about how the multithreaded layer and the event queue layer interact.

Note that these kinds of issues are universal to all GUI toolkits. Presuming an event dispatch model in your presenter/controller isn't tightly coupling you to only one specific GUI toolkit's concurrency model, it's coupling you to all of them. The event queueing interface shouldn't be that hard to abstract.

0
26

Because making the GUI lib thread safe is a massive headache and a bottleneck.

Control flow in GUIs often goes in 2 directions from the event queue to root window to the gui widgets and from the application code to the widget propagated up to the root window.

Devising a locking strategy that isn't lock the root window (will cause a lot of contention) is difficult. Locking bottom up while another thread is locking top down is a great way to achieve instant deadlock.

And checking if the current thread is the gui thread each time is costly and can cause confusion about what is actually happening with the gui, especially when you are doing a read update write sequence. That needs to have a lock on the data to avoid races.

10
  • 1
    Interestingly, I solve this problem by having a queuing framework for these updates that I wrote that manages the thread handoff.
    – durron597
    Sep 2, 2015 at 19:34
  • 2
    @durron597: And you never have any updates depending on current state of the UI another thread might influence? Then it might work. Sep 2, 2015 at 23:39
  • Why would you need nested locks? Why do you need to lock the entire root window when working on details in a child window? Lock order is a solvable problem by providing a single exposed method to lock mutliple locks in the right order (either top-down or bottom-up, but don't leave the choice to the caller)
    – MSalters
    Sep 3, 2015 at 12:07
  • 1
    @MSalters with root I meant the current window. To get all the locks you need to acquire you need to walk up the hierarchy which requires locking each container as you come across it getting the parent and unlocking (to ensure you only lock top-down) and then hope it hasn't changed on you after you get the root window and you do the lock top-down. Sep 3, 2015 at 12:24
  • 1
    @MSalters: Maybe also look at Karls's answer: The problem is that you have some things starting at the innermost child (user-input and other events), and others starting from the outside (mostly your updates). So unless you have exactly one lock (or at least have split the ui into clearly separated sections with just one lock), you will have locking errors (deadlock or data-race, whatever). Sep 3, 2015 at 13:47
17

Threadedness (in a shared memory model) is a property that tends to defy abstraction efforts. A simple example is a Set-type: while Contains(..) and Add(...) and Update(...) is a perfectly valid API in a single threaded scenario, the multi-threaded scenario needs a AddOrUpdate.

The same thing applies to the UI - if you want to display a list of items with a count of the items on top of the list, you need to update both on every change.

  1. The toolkit can't solve that problem as locking doesn't make sure the order of operations stays correct.
  2. The view can solve the problem, but only if you allow the business rule that the number on top of the list has to match the number of items in the list and only update the list through the view. Not exactly what MVC is supposed to be.
  3. The presenter can solve it, but needs to aware that the view has special needs with regards to threading.
  4. Databinding to a multi-threading capable model is another option. But that complicates the model with things that should be UI-concerns.

None of those looks really tempting. Making the presenter responsible for handling threading is recommended not because it is good, but because it works and the alternatives are worse.

2
  • Maybe a layer could be introduced in between the View and the Presenter that could also be swapped out.
    – durron597
    Sep 2, 2015 at 19:44
  • 2
    .NET has the System.Windows.Threading.Dispatcher to handle dispatching to both WPF and WinForms UI-Threads. That kind of layer between presenter and view definitely is useful. But it only provides toolkit independence, not threading independence.
    – Patrick
    Sep 2, 2015 at 19:51
10

I read a really good blog some time ago which discuss this issue (mentioned by Karl Bielefeldt), what it basically says is that it is very dangerous to try and make the UI kit thread safe, as it introduces possible deadlocks and depending on how it's implemented, race conditions into the framework.

There is also a performance consideration. Not so much now, but when Swing was first released it was heavily criticised for it's performance (been bad), but that wasn't really Swing's fault, it was people's lack of knowledge with how to use it.

SWT enforces the concept of thread safety by throwing exceptions if your violate it, not pretty, but at least you're made aware of it.

If you look into the painting process, for example, the order in which elements are painted is very important. You don't want the painting of one component to have a side effect on any other part of the screen. Imagine if you could update a label's text property, but it was painted by two different threads, you could end up with a corrupted output. So all painting is done within a single thread, normally based on the order of requirements/requests (but sometimes condensed to reduce the number of actual physical paint cycles)

You mention changing from Swing to JavaFX, but you'd have this problem with just about any UI framework (not just thick clients, but web as well), Swing just seems to be the one which highlights the problem.

You could design a intermediate layer (a controller of a controller?) whose job it is to ensure that calls to the UI are synchronised correctly. It's impossible to know exactly how you might design your non-UI parts of your API from the perspective of the UI API and most developers would complain that any thread protection implemented into the UI API was to restrictive or didn't meet their needs. Better to allow you to decide how you want to solve this issue, based on your needs

One of the biggest issues you need to consider is the ability to justify a given order of events, based on known inputs. For example, if the user resizes the window, the Event Queue model guarantees that a given order of events will occur, this might seem simple, but if the queue allowed events to triggered by other threads, then you can no longer guarantee the order in which the events might occur (a race condition) and all of sudden you have to start worrying about different states and not doing one thing until something else happens and you start having to share state flags around and you end up with spaghetti.

Okay, you might solve this by having some kind of queue which ordered the events based on the time they were issued, but isn't that what we already have? Besides, you could still no guarantee that thread B would generate it's events AFTER thread A

The main reason people get upset about having to think about their code, is because they are made to think about their code/design. "Why can't it be simpler?" It can't be simpler, because it's not a simple problem.

I remember when the PS3 was been released and Sony was up-talking the Cell processor and it's ability to perform separate lines of logics, decode audio, video, load and resolve model data. One game developer asked, "That's all awesome, but how do you synchronize the streams?"

The problem the developer was talking about was, at some point, all those separate streams would need to be synchronized down to single pipe for output. The poor presenter simply shrugged as it wasn't a issue they were familiar with. Obviously, they've had solutions to solve this problem now, but it has funny at the time.

Modern computers are taking a lot of input from a lot of different places simultaneously, all that input needs to be processed and delivered to the user in away which doesn't interfere with the presentation of other information, so it's a complex problem, with out a single simple solution.

Now, having the ability to switch frameworks, that's not an easy thing to design for, BUT, take MVC for a moment, MVC can be multi layered, that is, you could have a MVC which deals directly with managing the UI framework, you could then wrap that, again, in a higher layer MVC which deals with interactions with other (potentially multi threaded) frameworks, it would be the responsibility of this layer to determine how the lower MVC layer is notified/updated.

You would then use coding to interface design patterns and factory or builder patterns to construct these different layers. This means, that you multithreaded frameworks become decoupled from the UI layer through the use of a middle layer, as an idea.

11
  • 3
    You wouldn't have this problem with web. JavaScript deliberately has no threading support - a JavaScript runtime is effectively just one big event queue. (Yes, I know that strictly speaking JS has WebWorkers - these are nerfed threads, and behave more like actors in other languages).
    – James_pic
    Sep 3, 2015 at 9:10
  • 1
    @James_pic What you actually have is the part of the browser acting as the synchroniser for the event queue, which is basically what we've been talking about, the caller been responsible for ensuring that updates occur with the toolkits event queue Sep 3, 2015 at 9:41
  • Yes, exactly. The key difference, in the context of the web, is that this synchronisation occurs irrespective of the caller's code, because the runtime provides no mechanism that would allow code execution outside of the event queue. So the caller need not take responsibility for it. I believe that's also a large part of the motivation behind the development of NodeJS - if the runtime environment is an event loop, then all code is event loop aware by default.
    – James_pic
    Sep 3, 2015 at 12:38
  • 1
    That said, there are browser UI frameworks that have their own event-loop-within-an-event-loop (I'm looking at you Angular). Because these frameworks aren't protected by the runtime any more, callers must also ensure code is executed within the event loop, similar to with multithreaded code in other frameworks.
    – James_pic
    Sep 3, 2015 at 12:39
  • Would there be any particular problem with having "display-only" controls provide thread-safety automatically? If one has an editable text control which happens to be written by one thread and read by another, there's no good way one can make the write be visible to thread without synchronizing at least one of those actions to the actual UI state of the control, but would any such issues matter for display-only controls? I would think those could easily provide any needed locking or interlocks for operations performed on them, and allow the caller to ignore the timing of when...
    – supercat
    Sep 3, 2015 at 15:37
0

Being thread safe obviously implies being multithreaded. Without multithreading you don’t need threat safety.

So the question is: Do you want a multithreaded UI? You ask for the location of a button, and between you asking and getting a response someone moves the button. Or removes it.

At a lower level, you can be multithreaded again. A single thread that draws all windows can absolutely use multiple threads, one to draw each window. But that single UI thread control’s everything.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.