eclipse / xtext

Eclipse Xtext™ is a language development framework

Home Page:http://www.eclipse.org/Xtext

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

XtextDocumentLocker.notifyModelListenersOnUiThread() blocks UI thread waiting for lock

mx990 opened this issue · comments

When an XtextDocument is updated using modify(...), listeners on the UI thread are notified about the change. The same happens when readOnly(...) or one of its derivatives had to reconcile the state before the operation.

XtextDocumentLocker.notifyModelListenersOnUiThread() then blocks the UI thread waiting for a lock using tryReadOnly(...). This happens when it is not already running in the UI-thread, so it re-acquires the lock inside an asyncExec(...) call on the UI thread.

If there is a long-running operation holding the lock, this blocks the UI thread until the operation is completed, causing the UI to be unresponsive in the meantime. Since locks are actually always exclusive, this also happens for contending read operations.

This is also somewhat related to #2470, where the current behavior of notifyModelListenersOnUiThread() has also been identified as a potential cause for deadlocks.

In order to resolve the situation, I would suggest the following remedies:

  • use tryPriorityReadOnly(...) instead of tryReadOnly(...) to at least (attempt to) cancel other readers, although this currently only affects the validation job,
  • periodically run the event loop while waiting for the lock to at least prevent a totally unresponsive UI,
  • pass the lock held in the current thread to the UI thread before calling asyncExec(...),
  • allow listeners to opt-out of having the lock held for them and split the work inside the listeners between the resource and the UI.

I am happy to discuss possible solutions and help in implementing them.

I'm running into a similar issue in the case of a long-running validator and AbstractEObjectHover's getHoverRegion method. The behavior I'm seeing is that hovering over the editor while the validator is running will cause the UI to lock up, as the validator is holding a lock and the getHoverRegion call requests a read-only lock on the UI thread.

Please note that the underlying EMF model is not threadsafe, and there's no such thing as read-only access to the model. Each getter may or may not cause a modification - being it the instantiation of a multi-valued list or an attempt to resolve a proxy, loading a resource into the resource set. Code may attempt to access or modify adapter lists, etc. That's why exclusive access is necessary to safeguard against random bugs.

Do you know if your long-running validation periodically checks the cancel indicator?

Please note that the underlying EMF model is not threadsafe, and there's no such thing as read-only access to the model. Each getter may or may not cause a modification - being it the instantiation of a multi-valued list or an attempt to resolve a proxy, loading a resource into the resource set. Code may attempt to access or modify adapter lists, etc. That's why exclusive access is necessary to safeguard against random bugs.

I am painfully aware of these limitations in EMF. That is why my suggestions focused on keeping the UI responsive despite the lock, rather than removing the (exclusive) lock itself.

Do you have an opinion on how to improve the current situation in Xtext?

My long-running validator does not check the cancel indicator, and if relevant it is really calling out to an external program by creating a process object.

I agree with @mx990 above - in my case, I don't really care if hover info is broken while validation is running, so long as the UI doesn't lock up. Alternatively, maybe I shouldn't be using a validator for the stuff I'm doing?

Alternatively, maybe I shouldn't be using a validator for the stuff I'm doing?

That's hard to tell without knowing what you are doing specifically. Launching a process is certainly not something that I'd expect for a FAST validation.

My validator is marked as EXPENSIVE, so it only runs when the user explicitly requests it.

In the meantime, I bodged together a solution by subclassing DefaultCompositeHover and adding some state to my validator that indicates whether it is currently running. When the validator is running, my DefaultCompositeHover subclass returns null from getHoverRegion.