Bug 89965 - Should not be doing snapshot after full save
Summary: Should not be doing snapshot after full save
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows 2000
: P1 major (vote)
Target Milestone: 3.1 M7   Edit
Assignee: John Arthorne CLA
QA Contact:
URL:
Whiteboard:
Keywords: performance
Depends on:
Blocks:
 
Reported: 2005-04-01 10:45 EST by John Arthorne CLA
Modified: 2005-04-13 15:33 EDT (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description John Arthorne CLA 2005-04-01 10:45:35 EST
Build: ??

From reports in the eclipse.platform newsgroup.  It looks like a snapshot is
sometimes occuring after the FULL_SAVE when the IDE workbench shuts down.  If
true, this is bad news, because it will lead us to believe the previous session
crashed.  I need to investigate if this is actually happening.
Comment 1 Mark Melvin CLA 2005-04-01 10:59:11 EST
I originally posted this on eclipse.platform, so I am adding myself to the CC
list and this is my build information (I am using 3.1M5a):

Version: 3.1.0
Build id: I20050219-1500
Comment 2 Mark Melvin CLA 2005-04-01 11:13:30 EST
Here is the thread to date from the newsgroup.  For what it's worth, I have
tried placing the breakpoint inside the "if (hasTreeChanges)" block of the code
in SaveManager#snapshotIfNeeded(), but it never gets hit.  'hasTreeChanges' is
always false.  I guess this is a good thing...

I haven't tried simplifying this down yet, but it seems to me that the fact that
this block never gets executed doesn't matter to the case I was describing. 
Basically, if you add yourself as a save participant when you start up your
plugin, implement a participant the says it needs a delta (with
context.needDelta() in the #saving() method), but do *not* remove yourself as a
save participant when your plugin shuts down - you will never get a delta.  This
is because the platform always (well, almost always...) ends up calling
SaveManager#clearSavedDelta() on shutdown, and your save participant is
explicitly marked to not receive a delta.

If the proper way to do things is to always remove yourself as a save
participant when you shut down your plugin, that is perfectly acceptable - I
just did not see it in any of the docs or samples, which is why I ran into this
problem.


John Arthorne wrote:
> Thanks for these details. I should have asked you to put a breakpoint 
> instead just inside the "if (hasTreeChanges)" block.  These cases that 
> you show below should register as "no-op" operations, and it shouldn't 
> actually schedule the snapshot.
> 
> In any case, it doesn't look like anyone is actually modifying the 
> workspace during shutdown.  I will investigate this further in the 
> following bug report:
> 
> https://bugs.eclipse.org/bugs/show_bug.cgi?id=89965
> 
> One last detail I need from you: what version and build of Eclipse are 
> you using? This information is in the Help > About dialog.
> 
> Thanks again for your patience and help.
> -- 
> 
> Mark Melvin wrote:
> 
>> Sorry, I though that was repeatable, but now I get the following:
>>
>> First time I am in my save participant and get a FULL_SAVE.  I then 
>> set the breakpoint in .snapshotIfNeeded(), and resume to get the 
>> following stack trace:
>>
>> Thread [ModalContext] (Suspended (breakpoint at line 1119 in 
>> SaveManager))
>>     SaveManager.snapshotIfNeeded(boolean) line: 1119
>>     Workspace.endOperation(ISchedulingRule, boolean, IProgressMonitor) 
>> line: 919
>>     SaveManager.save(int, Project, IProgressMonitor) line: 979
>>     Workspace.save(boolean, IProgressMonitor) line: 1736
>>     IDEWorkbenchAdvisor$2.run(IProgressMonitor) line: 282
>>     ModalContext$ModalContextThread.run() line: 111
>>
>> Clicking resume, I end up right back in .snapshotIfNeeded() with the 
>> following stack trace:
>>
>> Thread [Worker-2] (Suspended (breakpoint at line 1119 in SaveManager))
>>     SaveManager.snapshotIfNeeded(boolean) line: 1119
>>     Workspace.endOperation(ISchedulingRule, boolean, IProgressMonitor) 
>> line: 919
>>     AutoBuildJob.doBuild(IProgressMonitor) line: 146
>>     AutoBuildJob.run(IProgressMonitor) line: 199
>>     Worker.run() line: 67
>>
>> Resuming a third time, I am again in .snapshotIfNeeded() with the 
>> following stack trace:
>>
>> Thread [System Bundle Shutdown] (Suspended (breakpoint at line 1119 in 
>> SaveManager))
>>     SaveManager.snapshotIfNeeded(boolean) line: 1119
>>     Workspace.endOperation(ISchedulingRule, boolean, IProgressMonitor) 
>> line: 919
>>     SaveManager.save(int, Project, IProgressMonitor) line: 979
>>     DelayedSnapshotJob.run(IProgressMonitor) line: 47
>>     SaveManager.shutdown(IProgressMonitor) line: 1108
>>     Workspace.close(IProgressMonitor) line: 323
>>     ResourcesPlugin.shutdown() line: 335
>>     PluginActivator.stop(BundleContext) line: 41
>>     BundleContextImpl$3.run() line: 1036
>>     AccessController.doPrivileged(PrivilegedExceptionAction) line: not 
>> available [native method]
>>     BundleContextImpl.stop() line: 1032
>>     BundleHost.stopWorker(boolean) line: 408
>>     BundleHost(AbstractBundle).stop() line: 420
>>     BundleStopper.basicStopBundles() line: 73
>>     BundleStopper.stopBundles() line: 62
>>     EclipseAdaptor.frameworkStopping(BundleContext) line: 665
>>     Framework.shutdown() line: 411
>>     SystemBundle$1.run() line: 171
>>     Thread.run() line: not available
>>
>> The fourth resume shuts down the platform.
>>
>> This is getting complicated to explain... ;o)
>> Mark.
>>
>>
>> Mark Melvin wrote:
>>
>>> Well, I must admit I am now getting out of my league...  I did as you 
>>> asked, and the first time my breakpoint is hit, it is during a 
>>> FULL_SAVE.  I put a breakpoint in the .snapshotIfNeeded() method and 
>>> this is the stack trace:
>>>
>>> Thread [ModalContext] (Suspended (breakpoint at line 1119 in 
>>> SaveManager))
>>>     SaveManager.snapshotIfNeeded(boolean) line: 1119
>>>     Workspace.endOperation(ISchedulingRule, boolean, 
>>> IProgressMonitor) line: 919
>>>     SaveManager.save(int, Project, IProgressMonitor) line: 979
>>>     Workspace.save(boolean, IProgressMonitor) line: 1736
>>>     IDEWorkbenchAdvisor$2.run(IProgressMonitor) line: 282
>>>     ModalContext$ModalContextThread.run() line: 111
>>>
>>>
>>> Clicking resume, I end up in my save participant again and I now have 
>>> a SNAPSHOT.  Resuming again puts me in the .snapshotIfNeeded() method 
>>> again with the following stack trace:
>>>
>>> Thread [Worker-2] (Suspended (breakpoint at line 1119 in SaveManager))
>>>     SaveManager.snapshotIfNeeded(boolean) line: 1119
>>>     Workspace.endOperation(ISchedulingRule, boolean, 
>>> IProgressMonitor) line: 919
>>>     AutoBuildJob.doBuild(IProgressMonitor) line: 146
>>>     AutoBuildJob.run(IProgressMonitor) line: 199
>>>     Worker.run() line: 67
>>>
>>>
>>> If you need more info on the stack traces, you'll have to tell me how 
>>> to do it. ;o)  I'm just right-clicking the suspended thread and 
>>> choosing "Copy Stack".
>>>
>>> Mark.
>>>
>>> John Arthorne wrote:
>>>
>>>> If you're willing to spend a bit more time debugging, here is what 
>>>> you can do:
>>>>
>>>> - Put a breakpoint in your save participant
>>>> - Shutdown the workbench
>>>> - When your breakpoint is hit, add a new breakpoint in 
>>>> org.eclipse.core.internal.resources.SaveManager#snapshotIfNeeded.  
>>>> This method should not be called after the save.  If the breakpoint 
>>>> is hit, reply here with the stack trace (or if it's in your code, 
>>>> fix it to avoid modifying the workspace during shutdown).
>>>> -- 
>>>>
>>>> Mark Melvin wrote:
>>>>
>>>>> Hmm...well I see a snapshot happen after the full save more often 
>>>>> than not, but I don't know why.  I am using 3.1M5a. Right now I 
>>>>> have over 20 plugins in my workspace that are all mine (and 
>>>>> obviously not part of the SDK).  As far as I know I am not 
>>>>> modifying the workspace when I shut down, and there are no errors 
>>>>> in the log, or any obvious signs of crashes.
>>>>>
>>>>> It *may* have something to do with my resource listener...but even 
>>>>> it doesn't modify the workspace.  It just marks an object as dirty 
>>>>> for when the next auto-build happens.
>>>>>
>>>>> Is there anything I can do to see what caused the snapshot to take 
>>>>> place?
>>>>>
>>>>> Mark.
>>>>>
>>>>> John Arthorne wrote:
>>>>>
>>>>>> One additional question: do you have any extra plugins installed 
>>>>>> in your workspace that don't belong to the Eclipse SDK?  One of 
>>>>>> these may be triggering that unwanted snapshot.
>>>>>> -- 
>>>>>>
>>>>>> John Arthorne wrote:
>>>>>>
>>>>>>> A snapshot should not be happening after the final save.  Does 
>>>>>>> this happen every time?  Is your save participant modifying the 
>>>>>>> workspace? What build of Eclipse are you using?  If a snapshot is 
>>>>>>> occurring after the final save, it's bad news.  This means the 
>>>>>>> platform, on next startup, will decide that the previous session 
>>>>>>> crashed, and will force a refresh of the workspace.  It will also 
>>>>>>> likely trigger a full build of the workspace.
>>>>>>> -- 
>>>>>>>
>>>>>>> Mark Melvin wrote:
>>>>>>>
>>>>>>>> OK, after much wading through the whole platform 
>>>>>>>> startup/shutdown, and the JDT core, I found the annoyingly 
>>>>>>>> simple answer to my problem...
>>>>>>>>
>>>>>>>> You *must* remove yourself as a save participant in your 
>>>>>>>> plugin's stop() method as well!  As far as I can see, this is 
>>>>>>>> stated nowhere in the API docs, or the books I have come across 
>>>>>>>> to date.  But it appears that if you do *not* remove your plugin 
>>>>>>>> as a save participant, when the platform does its final 
>>>>>>>> SNAPSHOT, it goes ahead and marks all remaining registered save 
>>>>>>>> participants to NOT receive deltas the next time the platform 
>>>>>>>> starts.  The comment in the code says:
>>>>>>>>
>>>>>>>> /**
>>>>>>>>  * Marks the current participants to not receive deltas next 
>>>>>>>> time they are registered
>>>>>>>>  * as save participants. This is done in order to maintain 
>>>>>>>> consistency if we crash
>>>>>>>>  * after a snapshot. It would force plug-ins to rebuild their 
>>>>>>>> state.
>>>>>>>>  */
>>>>>>>>
>>>>>>>> Anyway, problem solved.  I guess I should file a documentation 
>>>>>>>> bug or something here to save others some pain.
>>>>>>>>
>>>>>>>> Now I have the additional problem that my Ant build gets fired 
>>>>>>>> off when my plugin starts, and generates an 
>>>>>>>> AntSecurityException...hmmm...I'm assuming it is because it 
>>>>>>>> hasn't loaded the dependent plugins yet so I need to batch the 
>>>>>>>> build off in a Job or something.  Hopefully that will do it for me.
>>>>>>>>
>>>>>>>> Thanks for the help, John.
>>>>>>>>
>>>>>>>> Mark.
>>>>>>>>
>>>>>>>> Mark Melvin wrote:
>>>>>>>>
>>>>>>>>> Well, I've done a little more in-depth debugging and I am 
>>>>>>>>> seeing some interesting behaviour.  If I put a breakpoint in my 
>>>>>>>>> save participant, I can see that in the .saving() method I am 
>>>>>>>>> indeed calling context.needDelta(), and this sets the field 
>>>>>>>>> needDelta = true in the context in question.  This is all 
>>>>>>>>> good.  However, when I step through my plugin's start() method 
>>>>>>>>> after restarting the workbench, I can see that when I call 
>>>>>>>>> ResourcesPlugin.getWorkspace().addSaveParticipant(), I end up 
>>>>>>>>> in the .addParticipant() method of the class 
>>>>>>>>> org.eclipse.core.internal.resources.SaveManager, which obtains 
>>>>>>>>> a valid SavedState object from its HashMap for my particular 
>>>>>>>>> plugin (again, working as expected), but it then calls 
>>>>>>>>> .isDeltaCleared() on my plugin id, which returns "true"!  So, 
>>>>>>>>> it proceeds to throw out the saved state information and 
>>>>>>>>> returns null.  Why is this happening?  I can clearly see that I 
>>>>>>>>> told the platform I need a delta, and yet somehow it has saved 
>>>>>>>>> the fact that I want my delta to be cleared?  What's going on 
>>>>>>>>> here?
>>>>>>>>>
>>>>>>>>> Mark.
>>>>>>>>>
>>>>>>>>> Mark Melvin wrote:
>>>>>>>>>
>>>>>>>>>> (I forgot to quote the original when replying from the web 
>>>>>>>>>> interface last night...)
>>>>>>>>>>
>>>>>>>>>> Interesting.  That is what I am doing - I guess it is good to 
>>>>>>>>>> know I don't need to do all the messy state stuff...but I 
>>>>>>>>>> don't see why it isn't working.
>>>>>>>>>>
>>>>>>>>>> If there are no changes since the last time, will you get back 
>>>>>>>>>> "null" when you call addSaveParticipant(), or will you get an 
>>>>>>>>>> actual object with an empty delta in it (if that is even 
>>>>>>>>>> possible)?
>>>>>>>>>>
>>>>>>>>>> Mark.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> John Arthorne wrote:
>>>>>>>>>>
>>>>>>>>>>> Your save participant should implement the 
>>>>>>>>>>> saving(ISaveContext) method.  Inside it, just call 
>>>>>>>>>>> ISaveContext.needDelta() to indicate that you would like a 
>>>>>>>>>>> resource delta available to you on the next startup.
>>>>>>>>>>> -- 
>>>>>>>>>>>
>>>>>>>>>>> Mark Melvin wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi There,
>>>>>>>>>>>>
>>>>>>>>>>>> I am trying to have my plugin register as a save participant 
>>>>>>>>>>>> because it contains a resource listener that responds to 
>>>>>>>>>>>> workspace changes.  I have a couple of books (Java 
>>>>>>>>>>>> Developer's Guide to Eclipse 2nd Ed., and Building 
>>>>>>>>>>>> Commercial Quality Plugins), both of which simply say to 
>>>>>>>>>>>> register as a save participant at plugin startup, both 
>>>>>>>>>>>> showing the simple case where the implementation of 
>>>>>>>>>>>> ISaveParticipant simply calls context.needDelta() in the 
>>>>>>>>>>>> .saving() method.
>>>>>>>>>>>>
>>>>>>>>>>>> Well, I have done this, and the ISavedState object I get 
>>>>>>>>>>>> back when I register is always equal to "null".  I have seen 
>>>>>>>>>>>> once or twice where it wasn't - but generally, it is null - 
>>>>>>>>>>>> even if I have created a file or project in the meantime 
>>>>>>>>>>>> (which has caused the activation of my plugin).
>>>>>>>>>>>>
>>>>>>>>>>>> What confuses me is that in the Eclipse Help, they show all 
>>>>>>>>>>>> this wild creation of state files, generating save numbers, 
>>>>>>>>>>>> etc.  Is this *required* to simply get a delta since the 
>>>>>>>>>>>> last time my plugin was registered (God, I hope not... I 
>>>>>>>>>>>> don't want to have to maintain all these files 
>>>>>>>>>>>> manually...)?  If so, shouldn't these books mention 
>>>>>>>>>>>> something like this?  Also, if so, is there a nice working 
>>>>>>>>>>>> example of this somewhere?
>>>>>>>>>>>>
>>>>>>>>>>>> If not, what could I possibly be doing wrong?  This is my 
>>>>>>>>>>>> plugin's start() method:
>>>>>>>>>>>>
>>>>>>>>>>>>     public void start(BundleContext context) throws Exception {
>>>>>>>>>>>>         super.start(context);
>>>>>>>>>>>>         workspaceListener = new 
>>>>>>>>>>>> MyBuildModelWorkspaceListener();
>>>>>>>>>>>>         // Tell the platform we want to be a save 
>>>>>>>>>>>> participant, retrieving
>>>>>>>>>>>>         // the state, relative to the last time we were 
>>>>>>>>>>>> activated
>>>>>>>>>>>>         ISaveParticipant participant = new 
>>>>>>>>>>>> BasicWorkspaceSaveParticipant();
>>>>>>>>>>>>         ISavedState lastState = 
>>>>>>>>>>>>
ResourcesPlugin.getWorkspace().addSaveParticipant(MyPlugin.getDefault(), 
>>>>>>>>>>>> participant);
>>>>>>>>>>>>         if (lastState != null) {
>>>>>>>>>>>>             // Run any changes through our resource change 
>>>>>>>>>>>> listener
>>>>>>>>>>>>             
>>>>>>>>>>>> lastState.processResourceChangeEvents(workspaceListener);
>>>>>>>>>>>>         }
>>>>>>>>>>>>         // Register to listen to POST_CHANGE events
>>>>>>>>>>>>         
>>>>>>>>>>>> ResourcesPlugin.getWorkspace().addResourceChangeListener(
>>>>>>>>>>>>                         workspaceListener, 
>>>>>>>>>>>> IResourceChangeEvent.POST_CHANGE);
>>>>>>>>>>>>     }
>>>>>>>>>>>>
>>>>>>>>>>>> And, my BasicWorkspaceSaveParticipant class looks like this:
>>>>>>>>>>>>
>>>>>>>>>>>> public class BasicWorkspaceSaveParticipant implements 
>>>>>>>>>>>> ISaveParticipant {
>>>>>>>>>>>>
>>>>>>>>>>>>     public void doneSaving(ISaveContext context) {
>>>>>>>>>>>>         // TODO Auto-generated method stub
>>>>>>>>>>>>
>>>>>>>>>>>>     }
>>>>>>>>>>>>
>>>>>>>>>>>>     public void prepareToSave(ISaveContext context) throws 
>>>>>>>>>>>> CoreException {
>>>>>>>>>>>>         // TODO Auto-generated method stub
>>>>>>>>>>>>
>>>>>>>>>>>>     }
>>>>>>>>>>>>
>>>>>>>>>>>>     public void rollback(ISaveContext context) {
>>>>>>>>>>>>         // TODO Auto-generated method stub
>>>>>>>>>>>>
>>>>>>>>>>>>     }
>>>>>>>>>>>>
>>>>>>>>>>>>     public void saving(ISaveContext context) throws 
>>>>>>>>>>>> CoreException {
>>>>>>>>>>>>         // This tells the platform the next time we ask to be
>>>>>>>>>>>>         // a save participant (in our plugin's startup method)
>>>>>>>>>>>>         // we will be given a delta.
>>>>>>>>>>>>         context.needDelta();
>>>>>>>>>>>>     }
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> When my plugin starts, I *always* get lastState==null. I am 
>>>>>>>>>>>> not clearing the workspace or anything, either.  Any 
>>>>>>>>>>>> pointers would be appreciated.
>>>>>>>>>>>>
>>>>>>>>>>>> Mark.

Comment 3 John Arthorne CLA 2005-04-01 11:27:18 EST
I think you have actually uncovered a bug introduced in the 3.1 development
stream.  It is a good habit for you to be removing your save participant anyway
in your plugin shutdown() or stop() method, just as you would with any listener
mechanism.  However, the fact that a snapshot is actually occurring is a bug
(and I have confirmed this behaviour). I will investigate.
Comment 4 John Arthorne CLA 2005-04-01 12:19:25 EST
Here is what happens:

 - Make any change to the workspace, and a snapshot job is scheduled to run in
five minutes
 - If you shutdown during this period, a FULL_SAVE occurs, but the snapshot job
is still sleeping.  During ResourcesPlugin shutdown, we see that the snapshot
job has been scheduled, so we assume the workspace has unsaved changes and run
the snapshot.  
 - When the snapshot runs, it clears all delta requests from save participants,
because it will not be able to provide a correct delta unless a FULL_SAVE occurs.
 - On the next startup, we detect that a snapshot occurred after the last
FULL_SAVE, so we assume the workspace crashed, and do a full refresh of the
workspace.

In short, it means since about M4 we have done a full refresh of the workspace
on just about every startup.  This is why I've flagged the bug with the
performance keyword.

I have fixed this bug by always canceling the snapshot job at the time a full
save occurs.  This will prevent a snapshot from happening unless the workspace
actually does change between the time of the save and the time when the
resources plugin shuts down.

I have also made the workspace print a warning to the log file when it detects a
crash has occurred.  This will make it easier for us to diagnose bugs like this
in the future.

Unfortunately it is too late to release this fix for M6 (ships today).  This fix
will be in the first build after 3.1 M6.

Note: it is still good practice for you to remove your save participant when
your plugin shuts down.  This will effectively workaround your problem.  Great
find!!
Comment 5 John Arthorne CLA 2005-04-01 14:01:16 EST
Fix released to HEAD.
Comment 6 Tod Creasey CLA 2005-04-11 10:09:32 EDT
John what are the numbers for this?
Comment 7 Bogdan Gheorghe CLA 2005-04-13 15:33:44 EDT
*** Bug 91145 has been marked as a duplicate of this bug. ***