38

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.

durron597
  • 7,590
  • 9
  • 37
  • 67
  • 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 02 '15 at 19:21
  • 1
    @Ordous You can still ensure that the postings are minimal while putting the thread handoff in the view. – durron597 Sep 02 '15 at 19:23
  • @durron957 Yet distributing info among multiple components of the view is the job of the presenter, not the view? If you move this distribution of the minimized chunk of data into the view you're just moving the coupling from `view thread model in presenter` to `domain model in view`. – Ordous Sep 02 '15 at 19:26
  • 2
    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. – MadProgrammer Sep 02 '15 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 – MadProgrammer Sep 02 '15 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 – MadProgrammer Sep 02 '15 at 22:15
  • @MadProgrammer Thanks for stopping by! :) I was hoping you'd write an answer too, but I appreciate your comments. – durron597 Sep 02 '15 at 22:17
  • @durron597 I felt the provided answer were already pretty good, so I didn't want to clutter it up...instead got carried away with a very long comment :P – MadProgrammer Sep 02 '15 at 22:24
  • @MadProgrammer If those three comments were an answer, I'd upvote it :) I hope you can find the blog post too, I'd like to read it. – durron597 Sep 02 '15 at 22:27
  • It's good to note that many OS-native UI libraries already have this problem. X11 is ancient. Windows actually has a UI thread _per window_ but in practice everyone reuses the same thread for all windows. OSX is new enough that it's reasonably threadsafe, but still with some major exceptions – MSalters Sep 03 '15 at 12:02

4 Answers4

25

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.

ratchet freak
  • 25,706
  • 2
  • 62
  • 97
  • 1
    Interestingly, I solve this problem by having a queuing framework for these updates that I wrote that manages the thread handoff. – durron597 Sep 02 '15 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. – Deduplicator Sep 02 '15 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 03 '15 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. – ratchet freak Sep 03 '15 at 12:24
  • @ratchetfreak: If the child you try to lock is removed by another thread while you're locking it, that's just slightly unfortunate but not relevant to locking. You just can't operate on an object which another thread just removed. But why is that other thread removing objects/windows that your thread is still using? That's not good in any scenario, not just UI's. – MSalters Sep 03 '15 at 13:06
  • @MSalters a gui lib's locking mechanism would need to account for it either way. – ratchet freak Sep 03 '15 at 13:11
  • @MSalters: Maybe also look at [Karls's answer](http://programmers.stackexchange.com/a/295245): 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). – Deduplicator Sep 03 '15 at 13:47
  • @Deduplicator: I'm familiar with both patterns. But say that you're bubbling up an input event. You lock the innermost child, check if it wants it. If not, get the parent, hand it the event, and unlock the child. Don't process the event in the parent while holding the child lock. This avoids the ordering problem. Things get hairy when removing a window, but there you can agree that you need to lock parent and child (in that order) to remove the child. Once the child has accepted the event, it can't be removed until the event is handled or handed off to the parent, – MSalters Sep 03 '15 at 14:33
  • **further discussion should go in [chat](http://chat.stackexchange.com/rooms/21/the-whiteboard) please** – ratchet freak Sep 03 '15 at 14:34
  • @MSalters: So, the parent has the event and no locks, and someone locks the parent and invalidates handling the event to it in the first place. Sounds familiar? – Deduplicator Sep 03 '15 at 14:36
22

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.

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
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.

Patrick
  • 1,873
  • 11
  • 14
  • Maybe a layer could be introduced in between the View and the Presenter that could also be swapped out. – durron597 Sep 02 '15 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 02 '15 at 19:51
9

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.

  • 2
    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 03 '15 at 09: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 – MadProgrammer Sep 03 '15 at 09: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 03 '15 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 03 '15 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 03 '15 at 15:37
  • ...updates actually get rendered to the screen. For things like progress bars, it would be more convenient to say that a caller can call the "update" routine any time it's convenient and it will return immediately after ensuring that the control will get redrawn with the new data sometime in the future, than to have overly-frequent update requests either block/delay the caller or accumulate in an event queue. – supercat Sep 03 '15 at 15:39
  • @supercat The problem I've seen with this is, is people get confused about what is and isn't thread safe (this happens today in Swing). Better to simply you're not thread safe and make the caller deal with it – MadProgrammer Sep 03 '15 at 20:31
  • @supercat The problem with multiple threads changing a read only component is more to do with painting issues then much else, you also lose control over the order in which updates might occur. Any threading solution I can think of, eventually you won't like it. The problem is complex and this solution equally complex – MadProgrammer Sep 03 '15 at 20:42
  • @MadProgrammer: Simple rule: at any moment in time, the control will show some state that was set within the last 50ms or so. If a task is expected to perform thousands of operations per second, but might possibly get stalled (e.g. reading data from a CD-ROM), making status-display updates wait for the UI thread could slow things down by orders of magnitude if code updates the status before/after each operation; if code doesn't update status before/after each operation, the display won't show what operation is causing code to get stalled. Posting updates as events could flood... – supercat Sep 03 '15 at 20:46
  • ...the UI queue with lots of events that will be obsolete before they're ever processed. I can't think of any decent way of handling stuff without having thread-safety support in the control unless the control calls a "get status" callback 20+ times/second at its convenience and displays what the callback returns. That approach would work, but calling a polling routine continually while nothing is happening is kinda icky. – supercat Sep 03 '15 at 20:49
  • @supercat Again, that solution works for a specify set of user cases, what happens when you want to do something else? Usually, in this type of situation I use a consolidation method (on the caller side) which reduces the number of updates. This allows the control to be flexible under more conditions. The more restrictions you place on the UI, the less flexible it will become in the long run – MadProgrammer Sep 03 '15 at 21:36