Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 361279

Summary: [reconciling] JavaModel deltas (IJavaElementDelta) fail to be produced and reported to IElementChangedListener(s)
Product: [Eclipse Project] JDT Reporter: Stas Mising name <staslev>
Component: CoreAssignee: Jay Arthanareeswaran <jarthana>
Status: CLOSED WONTFIX QA Contact:
Severity: normal    
Priority: P3 CC: amj87.iitr, srikanth_sankaran
Version: 3.6   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard: stalebug
Attachments:
Description Flags
Testcase
none
TestCase results
none
TestCase results (cont.)
none
TestCase results (cont.)
none
TestCase results (cont.)
none
TestCase results (cont.) none

Description Stas Mising name CLA 2011-10-18 13:23:52 EDT
Build Identifier: I20100608-0911

Setup description:
------------------
In my setup the Java text editor is being updated both by a user input (keyboard), and a plugin I’m developing. The plugin uses the standard Display.getDefault().syncExec method to execute its changes to the Java Model (e.g. createMethod, createField, etc.).

The problem:
------------
Seems that under certain timing conditions, if the user and the plugin almost simultaneously add a new method to the Java Model, only one of new methods is reported to the IElementChangedListener (and is usually the one introduced by the plugin). This notification runs in the main thread and looks like :
[Working copy] Temp.java[*]: {CHILDREN | FINE GRAINED}
	Temp[*]: {CHILDREN | FINE GRAINED}
		foo()[+]: {}
Where foo is the method that the plugin introduced. I’d expect the second method to be reported much the same way. 
Sometimes it does, but under certain timing conditions, it does not, and the second method goes off the radar. The notification I get about it looks like so and does not provide the details I expect to be getting:
[Working copy] Temp.java[*]: {AST AFFECTED}
(This notification runs in the org.eclipse.jdt.internal.ui.text.JavaReconciler thread).

To sum things up, two methods are added to the JavaModel, one by a plugin and one using keyboard input. Only one method, that of the plugin’s, is reported in a detailed manner via a IJavaElementDelta to my IElementChangedListener, the other one does not trigger an elementChanged invocation with such delta.

Reproduction steps
------------------
It does not always happen and in fact requires some playing around to reproduce. However, I have managed to reproduce it multiple times and I'm quite positive of the findings. Basically seems that this happens when I time the plugin's and the user's updates to be as "simultaneous" as I can.

Any ideas?
I really do need to have both methods reported to my IElementChangedListener.

Reproducible: Sometimes
Comment 1 Ayushman Jain CLA 2011-10-20 01:58:38 EDT
Jay, can you please take a look? Thanks!
Comment 2 Stas Mising name CLA 2011-10-26 11:34:30 EDT
(In reply to comment #1)
> Jay, can you please take a look? Thanks!

Hi guys, hate to nag but I was wondering I there were any news.
Unfortunately this is a show stopper for my application, thus I'm quite enthusiastic about sorting it out.
Comment 3 Ayushman Jain CLA 2011-10-27 01:52:57 EDT
(In reply to comment #2)

Sorry for the delay on this. Unfortunately, Jay has been on vacation since this bug was opened. If you have zeroed in on the problem please feel free to investigate/post a patch. That will certainly make sure this is fixed sooner. Thanks!
Comment 4 Stas Mising name CLA 2011-10-29 08:03:31 EDT
The issue seems to stem from the fact that the JavaModel is being updated from a thread (say, the main thread) other than the reconciler thread, which the documentation neither forbids nor discourages as far as I've read (please feel free to correct me if I'm wrong here).

If the thread that has called the JavaModel API gets to CompilationUnit.makeConsistent (which it definitely can) before the reconciler thread does its magic, the reconciler can't detect any changes and fails to report any deltas (since the compilation unit had already been updated by the time the reconciler got to computing the deltas).

In my scenario I type in a new method in the editor, note that it does not get reconciled yet because the reconciler thread is deliberately suspended (for the sake of testing the described race condition).
Then a new method is created by using the JavaModel API, which in turn triggers a CompilationUnit.makeConsistent, which brings the method I've manually typed in into the compilation unit in order to operate on the up to date model. All this without ever reporting the delta corresponding to the method I've typed in manually (henceforth "the lost delta").

Following is the stack trace that triggered the CompilationUnit.makeConsistent after the JavaModel API had been used. 
(The reconsiler thread is in a suspended mode in the meanwhile.)

Thread [main] (Suspended)	
	CompilationUnit.makeConsistent(IProgressMonitor) line: 1064	
	CreateMethodOperation(CreateElementInCUOperation).parse(ICompilationUnit) line: 262	
	CreateMethodOperation(CreateElementInCUOperation).generateNewCompilationUnitAST(ICompilationUnit) line: 164	
	CreateMethodOperation(CreateElementInCUOperation).executeOperation() line: 127	
	CreateMethodOperation(JavaModelOperation).run(IProgressMonitor) line: 728	
	Workspace.run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor) line: 1975	
	CreateMethodOperation(JavaModelOperation).runOperation(IProgressMonitor) line: 793	
	SourceType.createMethod(String, IJavaElement, boolean, IProgressMonitor) line: 161	

I believe bug 63898 reports a similar problem, though the solution settled for synchronizing only the initialProcess() method of the reconciling flow (using the lock provided by getReconcilerLock()).
	
It would seem that in order to prevent the lost delta scenarios some sort of a synchronization mechanism is in order here. What comes to mind is either invoking the JavaModel API from the reconciler thread (how does one do that?), or synchronizing whatever thread the JavaModel API is invoked from, with the reconcier thread, perhaps by extending the use of the lock provided by getReconcilerLock().

I'd deeply appreciate your comments on this.

Thank you in advance.
Comment 5 Jay Arthanareeswaran CLA 2011-11-02 03:28:40 EDT
I will take a look at this now.
Comment 6 Srikanth Sankaran CLA 2011-11-30 00:17:36 EST
Since Jay had to go on unavoidable unplanned time off for several days,
this is not ready: retargetting to 3.8 M5.
Comment 7 Jay Arthanareeswaran CLA 2011-12-23 07:15:17 EST
Created attachment 208774 [details]
Testcase

Sorry Stas, it has taken me a while to get to this. I am attaching a testcase that I have come up with based on your description on the problem. The testcase has two threads, each trying to modify the same file, albeit in a different way. The first one simulates a editor change and the second one used Java model API. I tried the steps you said, including pausing the first reconciler thread while the second one allowed to run, but I can't quite see the problem. Can you please take a look at the testcase and confirm that this is inline with the scenario you described? Thanks!
Comment 8 Stas Mising name CLA 2011-12-27 05:28:51 EST
Hi Jay,

Thanks for taking a look.
I'll run this testcase ASAP and get back to you.

Meanwhile, I believe my scenario may require some deliberate thread suspending in order for the issue to manifest. I could think of the following breakpoints in order to lure it out.

First breakpoint:
CompilationUnit class, makeConsistent method

Second breakpoint:
AbstractReconciler class, run method (say the "process(r);" line)

My point being that if the makeConsistent is called first (while the reconciler is in the "process(r);" line) it will mess up the delta state for the "process(r);" once it runs.

The documentation (for IOpenable) also seems to mention this:

/**
 * Makes this element consistent with its underlying resource or buffer
 * by updating the element's structure and properties as necessary.
 *<p>
 * Note: Using this functionality on a working copy will interfere with any
 * subsequent reconciling operation. Indeed, the next
 * {@link ICompilationUnit#reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor)} or
 * {@link ICompilationUnit#reconcile(int, boolean, boolean, WorkingCopyOwner, IProgressMonitor)}
 * operation will not account for changes which occurred before an
 * explicit use of {@link #makeConsistent(IProgressMonitor)}
 * <p>
 * @param progress the given progress monitor
 * @exception JavaModelException if the element is unable to access the contents
 *      of its underlying resource. Reasons include:
 * <ul>
 *  <li>This Java element does not exist (ELEMENT_DOES_NOT_EXIST)</li>
 * </ul>
 * @see IOpenable#isConsistent()
 * @see ICompilationUnit#reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor)
 */
void makeConsistent(IProgressMonitor progress) throws JavaModelException;

Your help would be much appreciated.
Comment 9 Stas Mising name CLA 2011-12-29 10:39:29 EST
Created attachment 208850 [details]
TestCase results
Comment 10 Stas Mising name CLA 2011-12-29 10:40:07 EST
Created attachment 208851 [details]
TestCase results (cont.)
Comment 11 Stas Mising name CLA 2011-12-29 10:40:35 EST
Created attachment 208852 [details]
TestCase results (cont.)
Comment 12 Stas Mising name CLA 2011-12-29 10:40:48 EST
Created attachment 208853 [details]
TestCase results (cont.)
Comment 13 Stas Mising name CLA 2011-12-29 10:41:01 EST
Created attachment 208854 [details]
TestCase results (cont.)
Comment 14 Stas Mising name CLA 2011-12-29 11:00:02 EST
I've attached 5 separate tests case runs, each presents a different result.
All 5 runs were red.

Hope this helps.

Thank you in advance, 

Stas
Comment 15 Jay Arthanareeswaran CLA 2012-01-02 04:28:58 EST
(In reply to comment #14)
> I've attached 5 separate tests case runs, each presents a different result.
> All 5 runs were red.

Thanks for that, Stas. Looks like the 2nd and 4th are same failures. And so are the 3rd and 5th. So, we have 3 distinct failures. 

> The documentation (for IOpenable) also seems to mention this:

In fact, the ICompilationUnit#reconcile also mentions this behavior:

/**
 * Reconciles the contents of this working copy, sends out a Java delta
 * notification indicating the nature of the change of the working copy since
 * the last time it was either reconciled or made consistent
 * ({@link IOpenable#makeConsistent(IProgressMonitor)}), and returns a
 * compilation unit AST if requested.
 * <p>
...
*/
Comment 16 Jay Arthanareeswaran CLA 2012-01-18 23:52:20 EST
Stas, I don't think I will have enough time for this release. Hence moving out of 3.8. If you can come with an approach for the problem and a patch, we can surely consider that.
Comment 17 Eclipse Genie CLA 2019-12-10 16:53:43 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.