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

Bug 338667

Summary: refreshLocal does not trigger ResourceChangeEvent when timestamp doesn't change
Product: [Eclipse Project] Platform Reporter: Jens Baumgart <jens.baumgart>
Component: ResourcesAssignee: Platform-Resources-Inbox <platform-resources-inbox>
Status: CLOSED WONTFIX QA Contact:
Severity: normal    
Priority: P3 CC: jamesblackburn+eclipse, matthias.sohn, philipp.thun, robin.rosenberg
Version: 3.6.1   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard: stalebug

Description Jens Baumgart CLA 2011-03-02 09:05:04 EST
The underlying file of a resource is changed using java.io.File and refreshLocal is called afterwards. Not resource change event is fired if the last modification of the resource and the modification of the underlying file were done in the same second (Linux). On Windows, the problem appears if both modifications are done in the same millisecond.

The following failing test can be used to reproduce the problem.

package ztest;

import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.junit.Test;

public class TestFS {

	@Test
	public void test() throws Exception {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot workspaceRoot = workspace.getRoot();
		IProject project = workspaceRoot.getProject("TestProject");
		project.create(null);
		project.open(null);
		
		// write file test.txt with content content
		IFile file = project.getFile("test.txt");
		InputStream stream = new ByteArrayInputStream("file content".getBytes());
		file.create(stream, true, null);

		// install resource change listener
		final AtomicBoolean listenerCalled = new AtomicBoolean(false);
		workspace.addResourceChangeListener(new IResourceChangeListener() {
			@Override
			public void resourceChanged(IResourceChangeEvent event) {
				listenerCalled.set(true);
			}
		});
		
		// Thread.sleep(1100); <---- makes the test work
		
		// change file content using java.io.File
		File javaFile = file.getLocation().toFile();
		FileOutputStream outputStream = new FileOutputStream(javaFile);
		outputStream.write("changed content".getBytes());
		outputStream.close();
		
		// refresh the resource and expect a resource change event
		file.refreshLocal(IResource.DEPTH_ZERO, null);
		assertEquals(true, listenerCalled.get());
	}
	
}
Comment 1 James Blackburn CLA 2011-03-02 09:08:14 EST
This is NOT_ECLIPSE. Eclipse relies on the modification time to detect external changes.  If the modification stamp hasn't changed, then there isn't anyway for us to detect external modification...
Comment 2 James Blackburn CLA 2011-03-02 09:12:03 EST
(And if you've melodramatically modified the file externally and want to notify listeners, you can IResource#touch() the resource.)
Comment 3 James Blackburn CLA 2011-03-02 09:12:48 EST
(In reply to comment #2)
> ... melodramatically ...

Have no idea why I auto-typed that :$
Comment 4 John Arthorne CLA 2011-03-02 09:14:20 EST
Yes this is known behaviour. The only place we have hit this in practice is in test suites that rapidly create and modify files. We have a helper method in our tests for cases where we want an external change to be "noticed":

	while (file.isSynchronized(IResource.DEPTH_ZERO)) {
		modifyInFileSystem(file);
		Thread.sleep(100);
	}
Comment 5 Jens Baumgart CLA 2011-03-02 09:37:27 EST
On Ubuntu Linux the time stamp resolution is one second (on Windows 1 ms).
So it can happen in productive code that you change a resource, call a non Eclipse Library (e.g. JGit) and refresh afterwards and all this happens in one second.
Comment 6 James Blackburn CLA 2011-03-02 10:00:41 EST
(In reply to comment #5)
The point is that if the timestamp hasn't changed, there's no way for eclipse to know that the resource has changed.  How else would this work?

If you want to notify resource change listeners of a change you know has happened, then IResource#touch is your friend.
Comment 7 Matthias Sohn CLA 2011-03-03 05:07:32 EST
You could consider to also track the file length, this would decrease the likelihood to miss updates at the price of consuming some more memory to cache file lengths and one more stat() call per resource to check the file length in case the timestamp didn't change (if this check is done using Java code).
Comment 8 James Blackburn CLA 2011-05-19 05:54:16 EDT
Apparently this may happen quite a bit with egit with knock-on for any listeners expecting a delta (bug 346079 comment 15).  

(In reply to comment #7)
We already get the file length when we fetchInfo on the FileStore, so there would be no additional cost here. We would, however, need need to cache the length in the ResourceInfo which adds 1 'long' to every resource in the workspace tree.
Comment 9 Robin Rosenberg CLA 2011-05-19 15:52:58 EDT
(In reply to comment #8)
> Apparently this may happen quite a bit with egit with knock-on for any
> listeners expecting a delta (bug 346079 comment 15).  
> 
> (In reply to comment #7)
> We already get the file length when we fetchInfo on the FileStore, so there
> would be no additional cost here. We would, however, need need to cache the
> length in the ResourceInfo which adds 1 'long' to every resource in the
> workspace tree.

Length is useful as one hint, but it is not enough, we still need to actually
read the file for ambigous cases.

Git does on more thing. It notes when it last checked, and it that time is
later than the last time we checked we know the status already. 

If our noted check time is <= file time stamp and our rememberd file time stamp is the same we read the file to see if it has chamged.
Comment 10 James Blackburn CLA 2011-05-19 16:09:03 EDT
(In reply to comment #9)
> Length is useful as one hint, but it is not enough, we still need to actually
> read the file for ambigous cases.

This bug is about getting the resource notification right.  It's unlikely that a file will change and have the modification stamp not change. It's vanishingly unlikely that a file while change && the length will be the same && the modification stamp will be the same, isn't it?

For resource delta purposes, we really can't do much I/O.  By all means egit will need to read changed files (as a result of the delta) to work out if the file has really changed, but this isn't the job of core.resources.
Comment 11 Lars Vogel CLA 2019-11-14 02:13:06 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.

If the bug is still relevant, please remove the "stalebug" whiteboard tag.