Skip to content
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

Closed
pleku opened this issue Aug 28, 2019 · 14 comments · Fixed by #20255
Closed

Calling VaadinService.access or accessSynchronously leaks UI ThreadLocals #6349

pleku opened this issue Aug 28, 2019 · 14 comments · Fixed by #20255

Comments

@pleku
Copy link
Contributor

pleku commented Aug 28, 2019

@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.

@archiecobbs
Copy link
Contributor

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()

@archiecobbs
Copy link
Contributor

archiecobbs commented Oct 15, 2024

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 UI.getCurrent() returns the wrong value.

Moreover, this is trivial to fix!

Reproducer:

Video of the reproducer:

VaadinUIBug.mov

Here'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 {

@Legioth
Copy link
Member

Legioth commented Oct 16, 2024

Thanks for the reminder and for the PR!

Is there a problem with fixing this?? If so what is the problem??

I suspect it just wasn't critical enough when it was filed and then it feel through the cracks.
I don't see any problem other than a very small risk that existing code out there accidentally depends on the bug but I don't think that's reason enough to leave it broken.

@archiecobbs
Copy link
Contributor

I don't see any problem other than a very small risk that existing code out there accidentally depends on the bug but I don't think that's reason enough to leave it broken.

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.

@github-project-automation github-project-automation bot moved this from 🔖 Normal Priority (P2) to ✅ Closed in Vaadin Flow bugs & maintenance (Vaadin 10+) Oct 18, 2024
vaadin-bot pushed a commit that referenced this issue Oct 18, 2024
…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
vaadin-bot pushed a commit that referenced this issue Oct 18, 2024
…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
vaadin-bot pushed a commit that referenced this issue Oct 18, 2024
…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
vaadin-bot pushed a commit that referenced this issue Oct 18, 2024
…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
vaadin-bot pushed a commit that referenced this issue Oct 18, 2024
…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
vaadin-bot added a commit that referenced this issue Oct 18, 2024
…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]>
vaadin-bot added a commit that referenced this issue Oct 18, 2024
…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]>
vaadin-bot added a commit that referenced this issue Oct 18, 2024
…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]>
vaadin-bot added a commit that referenced this issue Oct 18, 2024
…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]>
mshabarov pushed a commit that referenced this issue Oct 18, 2024
…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]>
@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 14.12.2.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.3.19.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.4.14.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 23.5.8.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.5.1.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.6.0.alpha2 and is also targeting the upcoming stable 24.6.0 version.

@jameskerrtaskize
Copy link

@Legioth @archiecobbs
Do you know if its valid for us to call VaadinServletRequest.getCurrent() in a beforeEnter method? It always used to return a value but since this change, it may now return null.

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.

@archiecobbs
Copy link
Contributor

Do you know if its valid for us to call VaadinServletRequest.getCurrent() in a beforeEnter() method?

I don't think it's safe to assume, in general, that there is a "current request" when beforeEnter() is invoked.

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 UI in order to apply the new view to it, there won't be any HTTP request associated with this action, since it originated from a background thread.

@jameskerrtaskize
Copy link

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.

@jameskerrtaskize
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment