-
Notifications
You must be signed in to change notification settings - Fork 167
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
Calling VaadinService.access or accessSynchronously leaks UI ThreadLocals #6349
Comments
Proposed patch: diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java
index 237fae7553..80e40b7fe6 100644
--- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java
+++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java
@@ -2050,12 +2050,13 @@ public abstract class VaadinService implements Serializable {
FutureAccess pendingAccess;
// Dump all current instances, not only the ones dumped by setCurrent
Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
.getInstances();
+ CurrentInstance.clearAll();
CurrentInstance.setCurrent(session);
try {
while ((pendingAccess = session.getPendingAccessQueue()
.poll()) != null) {
if (!pendingAccess.isCancelled()) {
pendingAccess.run();
while ((pendingAccess = session.getPendingAccessQueue() |
This bug is over five years old and still happening. Is there a problem with fixing this?? If so what is the problem?? Just to be clear, one of the many side effects of this bug is that Moreover, this is trivial to fix! Reproducer:
Video of the reproducer: VaadinUIBug.movHere's a more correct patch: --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java
+++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java
@@ -2125,11 +2125,12 @@ public abstract class VaadinService implements Serializable {
// Dump all current instances, not only the ones dumped by setCurrent
Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
.getInstances();
- CurrentInstance.setCurrent(session);
try {
while ((pendingAccess = session.getPendingAccessQueue()
.poll()) != null) {
if (!pendingAccess.isCancelled()) {
+ CurrentInstance.clearAll();
+ CurrentInstance.setCurrent(session);
pendingAccess.run();
try { |
Thanks for the reminder and for the PR!
I suspect it just wasn't critical enough when it was filed and then it feel through the cracks. |
OK great. IMHO the biggest reason to apply this is that it introduces "randomness" (using air quotes because in some applications it never causes a problem, in some applications it always causes a problem, and in some application it's truly random) that makes other bugs much harder to reproduce and track down, like #20240 (which took me a over week to figure out). Please let me know if you need anything else from me to get this pushed through. Thanks. |
…20255) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349
…20255) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349
…20255) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349
…20255) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349
…20255) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349
…20255) (#20278) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349 Co-authored-by: Archie L. Cobbs <[email protected]>
…20255) (#20280) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349 Co-authored-by: Archie L. Cobbs <[email protected]>
…20255) (#20281) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349 Co-authored-by: Archie L. Cobbs <[email protected]>
…20255) (#20282) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349 Co-authored-by: Archie L. Cobbs <[email protected]>
…20255) (#20279) Commands enqueued by VaadinSession.access() in general have nothing to do with each other. The only thing they have in common is they share the same VaadinSession (and, by implication, VaadinService). Therefore, if command №1 invoked UI.setCurrent() and command №2 invokes UI.getCurrent(), command №2 should read null, not the random UI from command №1 that it has nothing to do with. Fixes #6349 Co-authored-by: Archie L. Cobbs <[email protected]>
This ticket/PR has been released with Vaadin 14.12.2. |
This ticket/PR has been released with Vaadin 24.3.19. |
This ticket/PR has been released with Vaadin 24.4.14. |
This ticket/PR has been released with Vaadin 23.5.8. |
This ticket/PR has been released with Vaadin 24.5.1. |
This ticket/PR has been released with Vaadin 24.6.0.alpha2 and is also targeting the upcoming stable 24.6.0 version. |
@Legioth @archiecobbs I can go into more detail but if you tell me that we should never call VaadinServletRequest.getCurrent() then we'll have to go and rethink. |
I don't think it's safe to assume, in general, that there is a "current request" when For example, a user may be looking at a screen that says "Operation in progress..." and waiting for some background task to complete. When that task finally does complete, it may then as a result install a new view to display the results of the operation. So while it will need to know a |
Thanks for the quick response. We had mistakenly thought that beforeEnter would run on a request thread. I can now see that it may run on any thread and it makes sense that VaadinServletRequest.getCurrent() should return null in that instance. |
Not that it matters but if you call access within another access call, the task also gets put into the queue and executed later. So it may still be the same [request] thread running the task but the request is null. It doesn't matter now that we know its not a valid thing to do anyway. |
@tmattsso commented on Wed Aug 28 2019
When the method is called, CurrentInstance will store a copy of all current instances, then override two objects; VaadinService and VaadinSession. If there is a UI object in the map, it is not cleared (current request and response might be affected too). This means that clients can call UI.getCurrent inside the access block and get a random UI instance.
Either the UI mapping should explicitly be set to null, or the map be cleared, before running client code. The latter would be better, because it will also clear (temporarily) any other mappings that a user might have done for another session.
The text was updated successfully, but these errors were encountered: