-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vaadin way: mention background threads #3753
Comments
@mvysny Thanks for your input! I was actually about to start writing about background threads today. Will take your comments into account. |
Good discussion opening, although I am not agreeing the best practice. I would avoid setting thread locals in the background threads at all cost and not passing UI instance there. I prefer to write in the views method that uses ui.access using getUI of the component to get the ui instance, hence I do not need UI.getCurrent() In Flow getUI returns optional, which is even better
That makes it clean to use background threads in Presenter I find this one of the few things in typical Vaadin application where separation of concern serves purpose. With this arrangement Presenter does not need know about UI, the background thread can be business logic only and the view does not need to care about business logic. |
Good points! Unfortunately The I agree The implementation of the UI Executor is very straightforward: public final class UIExecutor implements Executor {
@NotNull
private final UI ui;
public UIExecutor(@NotNull UI ui) {
this.ui = Objects.requireNonNull(ui);
}
@Override
public void execute(@NotNull Runnable command) {
ui.access(command::run);
// maybe handle UIDetachedExceptions in some clean way
}
} |
It is thread safe. That was exactly my point. |
You can also use
The UI or actually the component is not there if user was bored to wait and closed the browser, navigated elsewhere. |
According to my knowledge it's not. Consider calling The If you believe the above observations are incorrect, I'd be happy to go through a proof. The proof should also show that all calls made by |
Based on my 10 years experience getUI() has worked both in Vaadin 8 and newer versions in a robust way. I have used the pattern I described and endorsed many of our customers to follow it with success.
I somewhat agree. It works most of the time, and I have contributed some bugfix in components to enable this but I would still not recommend. The principle of separation of concern in the pattern I am endorsing also avoids this problem as you will keep the background threads in your business logic clean from UI code. |
Happy to hear that! Unfortunately that doesn't change the fact that |
This is not a problem as my recommendation is not to start background thread in the constructor but in enter() in Vaadin 8 or afterNavigation() in newer versions. I.e. after navigation is complete you are past the security barriers etc. So you are clear to do some backend activity. In constructor or beforeEnter it is premature. At this stage components are attached and getUI() returns correct value, unless the component is detached again (which was stated earlier). |
In this simple case the @Route(value = "")
public class MainView extends Composite<Component> implements AfterNavigationObserver {
@Override
protected Component initContent() {
final VerticalLayout vl = new VerticalLayout();
vl.add(new H4("Hi!"));
return vl;
}
@Override
public void afterNavigation(AfterNavigationEvent event) {
System.out.println("HI!");
MyExecutors.get().submit(() -> getUI().access());
}
} The reason for that is that:
But the above no longer holds if the view is mutated in any way after the background block is submitted to the Executor, for example removed from the parent. In such case the |
If you follow my example application further, one additional recommendation I have is to run background tasks as Future and when component is detached I will have something like
and The primary concern is not that the view may be composite, but if the component is detached, there is no longer point to push updates and consume thread resources, so we stop them immediately. This naturally means that if |
Since |
It gives only the ui reference and the actual things happen in access block, which has the lock. So nothing that requires lock is executed without one. |
It examines internal structures such as StateNode without having the UI lock obtained. That is thread-unsafe. The subsequent call of access() block is not significant. |
In addition to the potential races, I also recommend against using it from a background thread because it's a Pandora's box. It's much easier to explain and remember that all The fix is just to call the method while the session is locked and then referencing that effectively final value from the background thread. |
So doing it like this instead:
|
That pattern can have a race if the background job is stated e.g. in the constructor and it happens to run before the component has been attached. That's also a potential problem if the background job uses If you start the job from the constructor, then you need to use
It's typically better to start the job from some event (e.g. attach, click or navigation) rather than from the constructor. Specifically when using
You can use If you do things really properly, then you should also take care of unscheduling if the component is detached. This is particularly important when you set up a continuous subscription rather than a one-time task.
Can also use a separate And don't get me started on he possibility that the component is attached and then detached again during the same round trip... |
If we are here in the quest to find the Vaadin way and best practice, we should anyway recommend not to start the job in the constructor. Like I already pointed that afterNavigation probably is the best suited place. And as you point onAttach perhaps the second best.
Yeah, that is why I prefer afterNavigation. |
A side topic (we could extract this to enhancement ticket in Flow) we should add Component#access(..) method which reduces the boilerplate needed. So adding something like this to Component:
|
Doing threading right is vital but very hard and surprisingly lot of customers do it the wrong way. I think Vaadin way should definitely tackle this topic. A very basic proposal is attached.
In general, Vaadin UI framework (and many other UI frameworks, including Swing) is not thread-safe. It's not safe to call any UI code other than
ui.access()
from background threads. Doing it will introduce subtle threading bugs. Those are the nastiest types of bugs since they occur randomly and usually in production under heavy load, and therefore are almost impossible to reproduce and fix in a controlled dev environment.That implies that:
UI.getCurrent()
).The best way to run background threads from the UI is to follow these rules:
ThreadLocal
.UI
as a parameter, but can only use it to callui.access()
. Even better is to wrap the current UI in anExecutor
so that the UI instance is not exposed and programmers are not tempted to call additional methods on the UI instance. The thread would then receive theExecutor
instance.Additional links:
CC @peholmst
The text was updated successfully, but these errors were encountered: