eclipse / xtext

Eclipse Xtext™ is a language development framework
http://www.eclipse.org/Xtext
Eclipse Public License 2.0
764 stars 319 forks source link

XtextDocumentLocker.notifyModelListenersOnUiThread() blocks UI thread waiting for lock #2817

Open mx990 opened 11 months ago

mx990 commented 11 months ago

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 https://github.com/eclipse/xtext/issues/2470, where the current behavior of notifyModelListenersOnUiThread() has also been identified as a potential cause for deadlocks.

mx990 commented 11 months ago

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

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

mister-walter commented 10 months ago

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.

szarnekow commented 10 months ago

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?

mx990 commented 10 months ago

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?

mister-walter commented 10 months ago

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?

szarnekow commented 10 months ago

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.

mister-walter commented 10 months ago

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.