You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] UI freeze caused by deadlock in EditorsManager #175
Describe the bug
Hint: this is the very first time I had the issue and it didn't happen again after I restarted my Eclipse IDE.
I started Eclipse and got a UI freeze of > 2 minutes. When I looked at the current threads I got:
"main" #1 [37576] prio=6 os_prio=0 cpu=70109.38ms elapsed=778.17s tid=0x00000139ff75fcb0 nid=37576 waiting for monitor entry [0x0000000cc08fc000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.concurrent.ConcurrentHashMap.replaceNode(java.base@21.0.8/ConcurrentHashMap.java:1122)
- waiting to lock <0x0000000490000000> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
at java.util.concurrent.ConcurrentHashMap.remove(java.base@21.0.8/ConcurrentHashMap.java:1102)
at com.microsoft.copilot.eclipse.ui.completion.EditorsManager.disposeNesRenderManager(EditorsManager.java:206)
at com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener.partClosed(EditorLifecycleListener.java:131)
at org.eclipse.ui.internal.PartService$8.run(PartService.java:169)
...
"Worker-6: Copilot initialization" #101 [32008] prio=5 os_prio=0 cpu=31.25ms elapsed=620.09s tid=0x00000139f9d60c10 nid=32008 waiting on condition [0x0000000cc49fe000]
java.lang.Thread.State: TIMED_WAITING (parking)
at jdk.internal.misc.Unsafe.park(java.base@21.0.8/Native Method)
- parking to wait for <0x0000000490ccbc78> (a java.util.concurrent.Semaphore$NonfairSync)
at java.util.concurrent.locks.LockSupport.parkNanos(java.base@21.0.8/LockSupport.java:269)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@21.0.8/AbstractQueuedSynchronizer.java:756)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(java.base@21.0.8/AbstractQueuedSynchronizer.java:1126)
at java.util.concurrent.Semaphore.tryAcquire(java.base@21.0.8/Semaphore.java:415)
at org.eclipse.ui.internal.PendingSyncExec.acquire(PendingSyncExec.java:39)
at org.eclipse.ui.internal.PendingSyncExec.waitUntilExecuted(PendingSyncExec.java:88)
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:145)
at org.eclipse.swt.widgets.Display.syncExec(Display.java:4895)
at com.microsoft.copilot.eclipse.ui.utils.SwtUtils.invokeOnDisplayThread(SwtUtils.java:81)
at com.microsoft.copilot.eclipse.ui.nes.RenderManager.registerListeners(RenderManager.java:585)
at com.microsoft.copilot.eclipse.ui.nes.RenderManager.<init>(RenderManager.java:134)
at com.microsoft.copilot.eclipse.ui.completion.EditorsManager.lambda$0(EditorsManager.java:166)
at com.microsoft.copilot.eclipse.ui.completion.EditorsManager$$Lambda/0x00000139b65a5870.apply(Unknown Source)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(java.base@21.0.8/ConcurrentHashMap.java:1708)
- locked <0x0000000490000000> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
at com.microsoft.copilot.eclipse.ui.completion.EditorsManager.getOrCreateNesRenderManager(EditorsManager.java:165)
at com.microsoft.copilot.eclipse.ui.completion.EditorLifecycleListener.partActivated(EditorLifecycleListener.java:78)
at com.microsoft.copilot.eclipse.ui.CopilotUi.initCompletionHandlerForActiveEditor(CopilotUi.java:249)
at com.microsoft.copilot.eclipse.ui.CopilotUi$1.run(CopilotUi.java:122)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
Locked ownable synchronizers:
- None
The main thread is waiting on the lock 0x0000000490000000, which is owned by Worker-6: Copilot initialization.
Waiting for the UI thread to execute its runnable… but the UI thread is blocked on this thread's lock
Root Cause
ConcurrentHashMap.computeIfAbsent() (line 165 of EditorsManager.java) holds an internal bucket lock while executing the mapping function. That mapping function (new RenderManager(...)) calls Display.syncExec(), which blocks until the UI thread is free. But the UI thread is trying to remove() from the sameConcurrentHashMap, needing the same bucket lock → deadlock.
Worker-6: computeIfAbsent() ──holds lock──▶ syncExec() ──waits for UI thread──┐
│
UI thread: remove() ──waits for same lock──◀────────────────────────────────────┘
Fix Options
Don't do UI work inside computeIfAbsent — Construct the RenderManager outside the lambda, or defer registerListeners() so the map lock is released first:
RenderManagermgr = nesRenderManagers.computeIfAbsent(editor,
ed -> newRenderManager(languageServer, nesProvider, ed, /*deferInit=*/true));
mgr.ensureListenersRegistered(); // runs after lock is released
Use asyncExec instead of syncExec in RenderManager.registerListeners() — This avoids blocking on the UI thread while holding the map lock. But requires ensuring the manager is fully initialized before use.
Replace computeIfAbsent with manual check-then-put — Use get() + putIfAbsent() to avoid holding the bucket lock during construction:
Activity
jdneo commentedon May 12, 2026
Thank you for reporting. Attached a analysis from agent. Will look at this issue later.
Here's the analysis:
UI Freeze: Classic Deadlock in
EditorsManagerTwo threads are deadlocked
Thread 1 —
main(UI thread) —BLOCKEDEditorsManager.disposeNesRenderManager()→ConcurrentHashMap.remove()<0x0000000490000000>(aReservationNodeinside the map)Thread 2 —
Worker-6: Copilot initialization—TIMED_WAITINGEditorsManager.getOrCreateNesRenderManager()→ConcurrentHashMap.computeIfAbsent()<0x0000000490000000>(the sameReservationNode)RenderManager→registerListeners()→SwtUtils.invokeOnDisplayThread()→Display.syncExec()Root Cause
ConcurrentHashMap.computeIfAbsent()(line 165 ofEditorsManager.java) holds an internal bucket lock while executing the mapping function. That mapping function (new RenderManager(...)) callsDisplay.syncExec(), which blocks until the UI thread is free. But the UI thread is trying toremove()from the sameConcurrentHashMap, needing the same bucket lock → deadlock.Fix Options
Don't do UI work inside
computeIfAbsent— Construct theRenderManageroutside the lambda, or deferregisterListeners()so the map lock is released first:Use
asyncExecinstead ofsyncExecinRenderManager.registerListeners()— This avoids blocking on the UI thread while holding the map lock. But requires ensuring the manager is fully initialized before use.Replace
computeIfAbsentwith manual check-then-put — Useget()+putIfAbsent()to avoid holding the bucket lock during construction:Option 3 is the most straightforward fix — it eliminates the lock-while-syncExec pattern entirely and is a minimal change.
[-][Bug] UI Freeze when opened Eclipse[/-][+][Bug] UI freeze caused by deadlock in EditorsManager[/+]