Closed andrejkrajnc closed 1 week ago
Excellent catch, thank you. Let me take a look. I assume that the virtual thread is blocked at MainView:80
in responseFuture.get()
and remains blocked even after the UI is closed. However, upon closing of the UI, the MainView.onDetach()
should have been called, which should call executor.close()
which in turn shuts down the internal ExecutorService, which is running all virtual threads, which should kill and clean up the virtual thread. It could be that this is not done, and I need to figure out some other way. Let me investigate.
Yup, the virtual thread stacktrace, when blocked in the dialog, looks as follows:
#75 "Vaadin-VirtualThreadExecutor-com.vaadin.flow.component.UI@51d974b0" virtual
java.base/java.lang.VirtualThread.park(VirtualThread.java:596)
java.base/java.lang.System$2.parkVirtualThread(System.java:2643)
java.base/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:54)
java.base/java.util.concurrent.locks.LockSupport.park(LockSupport.java:219)
java.base/java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1864)
java.base/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3780)
java.base/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3725)
java.base/java.util.concurrent.CompletableFuture.waitingGet(CompletableFuture.java:1898)
java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2072)
com.vaadin.starter.skeleton.MainView.confirmDialog(MainView.java:80)
com.vaadin.starter.skeleton.MainView.lambda$new$0(MainView.java:40)
com.vaadin.starter.skeleton.loom.VaadinSuspendingExecutor.lambda$run$0(VaadinSuspendingExecutor.java:54)
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:317)
java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
java.base/java.lang.VirtualThread.run(VirtualThread.java:329)
Reproduced. Steps to reproduce:
jcmd Thread.dump_to_file
and observe it throwing com.vaadin.flow.component.UIDetachedException
Unfortunately jcmd doesn't show the stacktrace of UIDetachedException
, but it's possible to place a breakpoint into UIDetachedException
's constructor when running in debug mode and learn the following stacktrace:
<init>:30, UIDetachedException (com.vaadin.flow.component)
handleAccessDetach:464, UI (com.vaadin.flow.component)
access:564, UI (com.vaadin.flow.component)
access:551, UI (com.vaadin.flow.component)
execute:99, VaadinSuspendingExecutor$UIExecutor (com.vaadin.starter.skeleton.loom)
submitRunContinuation:264, VirtualThread (java.lang)
tryGetStackTrace:1012, VirtualThread (java.lang)
asyncGetStackTrace:953, VirtualThread (java.lang)
getStackTrace:2447, Thread (java.lang)
...
Fixed. The problem is as follows: when MainView.onDetach() closes the VaadinSuspendingExecutor
, it in turn needs to shut down the virtual thread executor. The Executor needs to interrupt()
all active virtual threads, in order to terminate them cleanly. VirtualThread.interrupt()
calls VirtualThread.unpark()
, which in turn calls VirtualThread.submitRunContinuation()
which in turn call this.
We are testing your “vaadin-loom” project.
If the blocking dialog is opened and we close the web browser then virtual thread remains in stack trace. The virtual thread is never closed/terminated.
It this case also the jcmd Thread.dump_to_file command does not work properly. On jcmd console there is an error “com.vaadin.flow.component.UIDetachedException” and in result there is unfinished stack trace for virtual thread.
Probably after UI detach the virtual threads should be removed.