Community
Participate
Working Groups
Build Identifier: WorkManager#getPreparedOperationDepth() may return incorrect answer due to race-condition. This, in turn, may lead to timing problems with resource change notifications. Reproducible: Always Steps to Reproduce: Consider two workspace runnables: one with "IWorkspaceRoot" scheduling rule, the other with "null" scheduling rule. Suppose they are run as top-level operations in two different threads concurrently. Let's imagine the following chain of events. The operation with "IWorkspaceRoot" scheduling rule begins to run and gets preempted just before the call "getWorkManager().endUnprotected()" in Workspace#run. Then the second operation begins to run and in turn gets preempted just after the line "getWorkManager().beginUnprotected()". The first operation resumes execution, calls "getWorkManager().endUnprotected()" and enters "endOperation()" method. Notice that when "depthOne" is computed, "workManager.getPreparedOperationDepth()" will return 2 (not 1) and therefore the operation will not be regarded as "top-level". Because of this, instead of synchronously notifying resource change listeners, an asynchronous "notify job" will be scheduled. This may lead to "interim inconsistency" in models such as the Java Model which are updated by delta processors. The issue described was encountered in "real life" when the following assertion failed: IWorkspaceRunnable runnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { // checkout Java project (uses Resources API) } }; getWorkspace().run(runnable, null); // in empty workspace IJavaElement[] javaProjects = javaModel.getChildren(); assertTrue(javaProjects.length > 0); because JDT's JREUpdateJob was interfering from another thread.
See also bug 306214, bug 249951 & bug 273676. In general you can make no assumption about when the resource change delta will fire. It may fire in the current resource changing thread or in another notification thread periodically. The WS guarantees that listeners will eventually be notified of changes, but not when. *** This bug has been marked as a duplicate of bug 249951 ***
Just wanted to be more precise that the notification problem described is just a consequence. The major issue is a kind of race condition on "preparedOperations" field in WorkManager. Also, I forgot to mention explicitly that the problem has nothing to do with the periodic notifications mechanism. But it has everything to do with the mechanism responsible for determining if the given workspace operation is a "top-level" operation. The sequence of events described illustrates that under certain conditions workspace operations from different threads may simultaneously update the "preparedOperations" field. As far as I can see this counter is not supposed to be "shared" among multiple threads. (In particular, the JavaDoc on WorkManager says "This class also tracks operation state for <i>each thread</i> that is involved in an operation. This includes prepared and running operation depth, auto-build strategy and cancel state." - emphasis is mine) The direct consequence is that an operation which is "top-level" may under certain conditions not be regarded as such by "finalization" machinery in Workspace#endOperation(). I believe this is a major concern that deserves bumping importance level and thorough consideration. Hoping for a word from John Arthorne.
(In reply to comment #2) > Just wanted to be more precise that the notification problem described is just > a consequence. The major issue is a kind of race condition on > "preparedOperations" field in WorkManager. Where is the race? AFAICS (open call-hierarchy on preparedOperations) the field is only ever accessed while WorkManager#lock is held, is there something I'm missing? > Also, I forgot to mention explicitly that the problem has nothing to do with > the periodic notifications mechanism. But it has everything to do with the > mechanism responsible for determining if the given workspace operation is a > "top-level" operation. Right, this is precisely the issue described in bug 273676 isn't it? If it's just that the event isn't always fired at the end of your top-level operation then, but is fired at some point in the future, then this is expected: see bug 273676 comment 2. > The sequence of events described illustrates that under certain conditions > workspace operations from different threads may simultaneously update the > "preparedOperations" field. As far as I can see this counter is not supposed to > be "shared" among multiple threads. (In particular, the JavaDoc on WorkManager > says "This class also tracks operation state for <i>each thread</i> that is > involved in an operation. This includes prepared and running operation depth, > auto-build strategy and cancel state." - emphasis is mine) Ok, the JavaDoc should likely be updated. Does the discussion in bug 273676 help? In particular bug 273676 comment 3: the preparedOperations count keeps count of all operations currently checked into the workspace (irrespective of thread). When this decrements to 0, or enough time has elapsed, the workspace performs a notification. Even WorkspaceJobs with null scheduling rules are 'checked-in' to the workspace and contribute to the number of prepared operations.
Thank you very much, James! You were very helpful indeed. I really missed bug 273676, my apologies. But if (as John said) this is intentional and desired behavior, how should the code like I described be structured? Something like the following: IWorkspaceRunnable runnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { // checkout Java project (uses Resources API) } }; getWorkspace().run(runnable, null); // in empty workspace IJavaElement[] javaProjects = null; for (int i = 0; i < N; i++) // what is a good N? { javaProjects = javaModel.getChildren(); if (javaProjects.length > 0) break; Thread.sleep(100); } if (javaProject.length == 0) fail(); // do something with the created Java project (using JDT API) or is there a better "work-around"?
Presumably you need to register a listener with the JavaModel to tell you when any of its elements have changed. See also: http://help.eclipse.org/helios/nftopic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/IJavaElementDelta.html#ADDED http://dev.eclipse.org/newslists/news.eclipse.tools/msg53342.html
Well, it gets more complicated than I thought it would be. The control flow which I believed to be purely synchronous becomes an asynchronous one. Thank you for your replies James! I learned quite a bit from this "bug".
Thanks for explaining this James. Vladimir, you could use IWorkspace#checkpoint(false) to ensure the java model is reconciled after your checkout.
John, I really can't express how I'm grateful for your invaluable (as always) help! I did not know about this facility! Thank you!!!