Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
mx990 opened this issue Oct 5, 2023 · 7 comments

Comments

@mx990
Copy link

mx990 commented Oct 5, 2023

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.

@mx990
Copy link
Author

mx990 commented Oct 5, 2023

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.

@mister-walter
Copy link

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
Copy link
Contributor

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
Copy link
Author

mx990 commented Oct 17, 2023

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
Copy link

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
Copy link
Contributor

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
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants