[Bug] UI freeze caused by deadlock in EditorsManager · Issue #175 · microsoft/copilot-for-eclipse · GitHub
Skip to content

[Bug] UI freeze caused by deadlock in EditorsManager #175

Closed

Description

Environment

  • OS: Windows 11
  • Eclipse Version: 4.40.0.v20260423-1800 (I-BUILD: I20260423-1800)
  • Plugin Version: 0.16.0.202604230218_nightly

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.

Thread dump: threaddump-1778571792312.zip

To Reproduce
Open Eclipse

Expected behavior
It shouldn't hang.

Activity

  1. jdneo commented on May 12, 2026

    @jdneo
    Member

    Thank you for reporting. Attached a analysis from agent. Will look at this issue later.

    Here's the analysis:

    UI Freeze: Classic Deadlock in EditorsManager

    Two threads are deadlocked

    Thread 1 — main (UI thread) — BLOCKED

    • User closes an editor tab (click on ✕)
    • Calls EditorsManager.disposeNesRenderManager() → ConcurrentHashMap.remove()
    • Waiting to lock <0x0000000490000000> (a ReservationNode inside the map)

    Thread 2 — Worker-6: Copilot initialization — TIMED_WAITING

    • Calls EditorsManager.getOrCreateNesRenderManager() → ConcurrentHashMap.computeIfAbsent()
    • Holds lock <0x0000000490000000> (the same ReservationNode)
    • Inside the lambda, creates RenderManager → registerListeners() → SwtUtils.invokeOnDisplayThread() → Display.syncExec()
    • 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 same ConcurrentHashMap, 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

    1. Don't do UI work inside computeIfAbsent — Construct the RenderManager outside the lambda, or defer registerListeners() so the map lock is released first:

      RenderManager mgr = nesRenderManagers.computeIfAbsent(editor,
          ed -> new RenderManager(languageServer, nesProvider, ed, /*deferInit=*/true));
      mgr.ensureListenersRegistered(); // runs after lock is released
    2. 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.

    3. Replace computeIfAbsent with manual check-then-put — Use get() + putIfAbsent() to avoid holding the bucket lock during construction:

      RenderManager mgr = nesRenderManagers.get(editor);
      if (mgr == null) {
        mgr = new RenderManager(languageServer, nesProvider, editor);
        RenderManager existing = nesRenderManagers.putIfAbsent(editor, mgr);
        if (existing != null) {
          mgr.dispose();
          mgr = existing;
        }
      }
      return mgr;

    Option 3 is the most straightforward fix — it eliminates the lock-while-syncExec pattern entirely and is a minimal change.

  2. changed the title [-][Bug] UI Freeze when opened Eclipse[/-] [+][Bug] UI freeze caused by deadlock in EditorsManager[/+] on May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions