Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimeon Andreev2018-06-29 12:13:02 +0000
committerAndrey Loskutov2018-07-13 09:22:55 +0000
commit6b66a8c4be797630c95c517d32c27ef9f9c09918 (patch)
treef6084f7847755c1c545c6d2079769d5ce1e7e8b7
parent13c20760f8ae283a8092e9624e1408a02fec421a (diff)
downloadeclipse.platform.resources-6b66a8c4be797630c95c517d32c27ef9f9c09918.tar.gz
eclipse.platform.resources-6b66a8c4be797630c95c517d32c27ef9f9c09918.tar.xz
eclipse.platform.resources-6b66a8c4be797630c95c517d32c27ef9f9c09918.zip
Bug 530868 - Higher timestamp resolution with native providerI20180714-1500I20180713-2000
In order to obtain a better file timestamp precision, currently its necessary to use -Declipse.filesystem.useNatives=false which slows down builds considerably. This change introduces full use of Unix stat() on Linux systems. Since Kernel version 2.5.48, nano seconds are available from stat(). With this, the file timestamp from the native file system provider has milliseconds precision instead of seconds precision. For backward compatibility, the following option restores the seconds precision: -Declipse.filesystem.useNatives.modificationTimestampMillisecondsResolution=false Additionally, access to fields of StructStat is only done once (upfront), as opposed to on each access. This ensures performance of the Eclipse native file info provider is on par with the respective JDK provider. Change-Id: I92fe6834d53b6f6c9f54c697c69594212dc535a9 Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
-rw-r--r--bundles/org.eclipse.core.filesystem.linux.x86/os/linux/x86/libunixfile_1_0_0.sobin6292 -> 9608 bytes
-rwxr-xr-x[-rw-r--r--]bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.sobin8248 -> 10320 bytes
-rwxr-xr-x[-rw-r--r--]bundles/org.eclipse.core.filesystem.macosx/os/macosx/libunixfile_1_0_0.jnilibbin54936 -> 14920 bytes
-rw-r--r--bundles/org.eclipse.core.filesystem/natives/unix/macosx/Makefile8
-rw-r--r--bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c60
-rw-r--r--bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h8
-rw-r--r--bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java9
-rw-r--r--bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java3
-rw-r--r--tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IFileTest.java8
-rw-r--r--tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java1
-rw-r--r--tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_530868.java101
11 files changed, 182 insertions, 16 deletions
diff --git a/bundles/org.eclipse.core.filesystem.linux.x86/os/linux/x86/libunixfile_1_0_0.so b/bundles/org.eclipse.core.filesystem.linux.x86/os/linux/x86/libunixfile_1_0_0.so
index 0f258d08d..ddcc2d497 100644
--- a/bundles/org.eclipse.core.filesystem.linux.x86/os/linux/x86/libunixfile_1_0_0.so
+++ b/bundles/org.eclipse.core.filesystem.linux.x86/os/linux/x86/libunixfile_1_0_0.so
Binary files differ
diff --git a/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so b/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so
index cbff68544..9067085df 100644..100755
--- a/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so
+++ b/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so
Binary files differ
diff --git a/bundles/org.eclipse.core.filesystem.macosx/os/macosx/libunixfile_1_0_0.jnilib b/bundles/org.eclipse.core.filesystem.macosx/os/macosx/libunixfile_1_0_0.jnilib
index 2334802c5..15f0a20e4 100644..100755
--- a/bundles/org.eclipse.core.filesystem.macosx/os/macosx/libunixfile_1_0_0.jnilib
+++ b/bundles/org.eclipse.core.filesystem.macosx/os/macosx/libunixfile_1_0_0.jnilib
Binary files differ
diff --git a/bundles/org.eclipse.core.filesystem/natives/unix/macosx/Makefile b/bundles/org.eclipse.core.filesystem/natives/unix/macosx/Makefile
index cff6f86ba..4e0e92abc 100644
--- a/bundles/org.eclipse.core.filesystem/natives/unix/macosx/Makefile
+++ b/bundles/org.eclipse.core.filesystem/natives/unix/macosx/Makefile
@@ -14,10 +14,14 @@
CORE.C=../unixfile.c
LIB_NAME_FULL=libunixfile_1_0_0.jnilib
-JDK_INCLUDE=-I /System/Library/Frameworks/JavaVM.framework/Headers
+JAVA_HOME=$(shell /usr/libexec/java_home)
+
+JDK_INCLUDE=-I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin
+MACOSX_DEPLOYMENT_TARGET=10.10
+
FRAMEWORKS=-framework JavaVM -framework CoreServices
# define MACOSX to include Mac OS X specific code
-CC_FLAGS=-arch x86_64 -mmacosx-version-min=10.4 -DMACOSX
+CC_FLAGS=-arch x86_64 -mmacosx-version-min=10.10 -DMACOSX
core:
cc $(JDK_INCLUDE) $(CORE.C) -o $(LIB_NAME_FULL) -bundle $(FRAMEWORKS) $(CC_FLAGS)
diff --git a/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c b/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c
index 43d6aa561..32758b9ad 100644
--- a/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c
+++ b/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c
@@ -24,6 +24,40 @@
#include "unixfile.h"
+/* The lstat stat field st_mode. */
+static jfieldID attrs_st_mode;
+/* The lstat stat field st_size. */
+static jfieldID attrs_st_size;
+/* The lstat stat field st_mtime_sec. */
+static jfieldID attrs_st_mtime;
+/* The lstat stat field st_mtime_nsec divided by 1 000 000. Only filled on Linux based operating systems. */
+static jfieldID attrs_st_mtime_msec;
+/* Only filled on MACOSX. */
+static jfieldID attrs_st_flags;
+
+/*
+ * Class: Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_initializeStructStatFieldIDs
+ * Method: initializeStructStatFieldIDs
+ * Signature: ()V
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_initializeStructStatFieldIDs
+ (JNIEnv *env, jclass clazz)
+{
+ jclass structStatClass = (*env)->FindClass(env, "org/eclipse/core/internal/filesystem/local/unix/StructStat");
+
+ attrs_st_mode = (*env)->GetFieldID(env, structStatClass, "st_mode", "I");
+ attrs_st_size = (*env)->GetFieldID(env, structStatClass, "st_size", "J");
+ attrs_st_mtime = (*env)->GetFieldID(env, structStatClass, "st_mtime", "J");
+
+#ifdef MACOSX
+ attrs_st_flags = (*env)->GetFieldID(env, structStatClass, "st_flags", "J");
+#endif
+
+#ifndef MACOSX
+ attrs_st_mtime_msec = (*env)->GetFieldID(env, structStatClass, "st_mtime_msec", "J");
+#endif
+}
+
/*
* Get a null-terminated byte array from a java byte array. The returned bytearray
* needs to be freed when not used anymore. Use free(result) to do that.
@@ -54,22 +88,24 @@ jint convertStatToObject(JNIEnv *env, struct stat info, jobject stat_object)
cls = (*env)->GetObjectClass(env, stat_object);
if (cls == 0) return -1;
- fid = (*env)->GetFieldID(env, cls, "st_mode", "I");
- if (fid == 0) return -1;
- (*env)->SetIntField(env, stat_object, fid, info.st_mode);
+ if (attrs_st_mode == 0) return -1;
+ (*env)->SetIntField(env, stat_object, attrs_st_mode, info.st_mode);
+
+ if (attrs_st_size == 0) return -1;
+ (*env)->SetLongField(env, stat_object, attrs_st_size, info.st_size);
- fid = (*env)->GetFieldID(env, cls, "st_size", "J");
- if (fid == 0) return -1;
- (*env)->SetLongField(env, stat_object, fid, info.st_size);
+ if (attrs_st_mtime == 0) return -1;
+ (*env)->SetLongField(env, stat_object, attrs_st_mtime, info.st_mtime);
+
+#ifndef MACOSX
+ if (attrs_st_mtime_msec == 0) return -1;
+ (*env)->SetLongField(env, stat_object, attrs_st_mtime_msec, (info.st_mtim.tv_nsec / (1000 * 1000)));
+#endif
- fid = (*env)->GetFieldID(env, cls, "st_mtime", "J");
- if (fid == 0) return -1;
- (*env)->SetLongField(env, stat_object, fid, info.st_mtime);
#ifdef MACOSX
- fid = (*env)->GetFieldID(env, cls, "st_flags", "J");
- if (fid == 0) return -1;
- (*env)->SetLongField(env, stat_object, fid, info.st_flags);
+ if (attrs_st_flags == 0) return -1;
+ (*env)->SetLongField(env, stat_object, attrs_st_flags, info.st_flags);
#endif
return 0;
diff --git a/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h b/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h
index b76faefbb..7bfc207bd 100644
--- a/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h
+++ b/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h
@@ -37,6 +37,14 @@ extern "C" {
#endif
/*
+ * Class: Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_initializeStructStatFieldIDs
+ * Method: initializeStructStatFieldIDs
+ * Signature: ()V
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_initializeStructStatFieldIDs
+ (JNIEnv *env, jclass clazz);
+
+/*
* Class: org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives
* Method: chmod
* Signature: ([BI)I
diff --git a/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java b/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java
index 7321bd757..457560c9d 100644
--- a/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java
+++ b/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java
@@ -19,16 +19,23 @@ import org.eclipse.core.filesystem.provider.FileInfo;
*/
public class StructStat {
+ private static final boolean USE_MILLISECOND_RESOLUTION = Boolean.valueOf(System.getProperty("eclipse.filesystem.useNatives.modificationTimestampMillisecondsResolution", "true")).booleanValue(); //$NON-NLS-1$ //$NON-NLS-2$
+
public int st_mode;
public long st_size;
public long st_mtime;
+ public long st_mtime_msec; // millisecond component of the file timestamp, filled on Linux systems
public long st_flags; // Filled only on Mac OS X
public FileInfo toFileInfo() {
FileInfo info = new FileInfo();
info.setExists(true);
info.setLength(st_size);
- info.setLastModified(st_mtime * 1000);
+ long lastModified = (st_mtime * 1_000);
+ if (USE_MILLISECOND_RESOLUTION) {
+ lastModified += st_mtime_msec;
+ }
+ info.setLastModified(lastModified);
if ((st_mode & UnixFileFlags.S_IFMT) == UnixFileFlags.S_IFDIR)
info.setDirectory(true);
if ((st_flags & (UnixFileFlags.UF_IMMUTABLE | UnixFileFlags.SF_IMMUTABLE)) != 0)
diff --git a/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java b/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java
index ef47cc2dc..4b67ee653 100644
--- a/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java
+++ b/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java
@@ -38,6 +38,7 @@ public abstract class UnixFileNatives {
try {
System.loadLibrary(LIBRARY_NAME);
_usingNatives = true;
+ initializeStructStatFieldIDs();
_libattr = libattr();
} catch (UnsatisfiedLinkError e) {
if (isLibraryPresent())
@@ -187,6 +188,8 @@ public abstract class UnixFileNatives {
return (libattr & attr) != 0;
}
+ private static final native void initializeStructStatFieldIDs();
+
private static final native int chmod(byte[] path, int mode);
private static final native int chflags(byte[] path, int flags);
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IFileTest.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IFileTest.java
index 4c9eb711f..42e8fba73 100644
--- a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IFileTest.java
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IFileTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -980,6 +980,12 @@ public class IFileTest extends ResourceTest {
((FussyProgressMonitor) monitor).prepare();
}
file.setContents(stream, force, false, monitor);
+ boolean isOutOfSyncTestFile = outOfSync(file);
+ if (isOutOfSyncTestFile) {
+ // The test expects this file to be out of sync, when modified on file system
+ // level later on. We wait a moment to let this actually be the case.
+ Thread.sleep(10);
+ }
if (monitor instanceof FussyProgressMonitor) {
((FussyProgressMonitor) monitor).sanityCheck();
}
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
index 50ba99b11..b553c801d 100644
--- a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java
@@ -77,6 +77,7 @@ public class AllTests extends TestCase {
suite.addTest(PR_1GEAB3C_Test.suite());
suite.addTest(PR_1GH2B0N_Test.suite());
suite.addTest(PR_1GHOM0N_Test.suite());
+ suite.addTest(Bug_530868.suite());
return suite;
}
}
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_530868.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_530868.java
new file mode 100644
index 000000000..5553a48bd
--- /dev/null
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/Bug_530868.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Simeon Andreev and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Simeon Andreev - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.resources.regression;
+
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.ByteArrayInputStream;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.filesystem.provider.FileInfo;
+import org.eclipse.core.internal.filesystem.local.LocalFileNativesManager;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.tests.resources.ResourceTest;
+
+
+/**
+ * Test for Bug 530868: millisecond resolution of file timestamps with native
+ * provider.
+ */
+public class Bug_530868 extends ResourceTest {
+
+ private IProject testProject;
+ private IFile testFile;
+
+ public static Test suite() {
+ return new TestSuite(Bug_530868.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ testProject = getWorkspace().getRoot().getProject(Bug_530868.class + "TestProject");
+ testProject.create(getMonitor());
+ testProject.open(getMonitor());
+ testFile = testProject.getFile(getName());
+
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ testProject.delete(true, getMonitor());
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ /**
+ * Create a file several times and check that we see different modification
+ * timestamps.
+ */
+ public void testMillisecondResolution() throws Exception {
+ assertTrue("can only run if native provider is used", LocalFileNativesManager.isUsingNatives());
+
+ /*
+ * Run 3 times in case we have seconds resolution due to a bug, but by chance we
+ * happened to modify the file in-between two seconds.
+ */
+ long timestamp1 = modifyTestFileAndFetchTimestamp("some contents 1");
+ Thread.sleep(50);
+ long timestamp2 = modifyTestFileAndFetchTimestamp("some contents 2");
+ Thread.sleep(50);
+ long timestamp3 = modifyTestFileAndFetchTimestamp("some contents 3");
+
+ String failMessage = "expected different timestamps for modifications in quick succession";
+ assertNotEquals(failMessage, timestamp1, timestamp2);
+ assertNotEquals(failMessage, timestamp2, timestamp3);
+ }
+
+ private long modifyTestFileAndFetchTimestamp(String contents) throws Exception {
+ setTestFileContents(contents);
+ long timestamp = getLastModificationTimestamp();
+ return timestamp;
+ }
+
+ private void setTestFileContents(String contents) throws Exception {
+ ByteArrayInputStream contentsStream = new ByteArrayInputStream(String.valueOf(contents).getBytes());
+ if (testFile.exists()) {
+ testFile.delete(true, getMonitor());
+ }
+ testFile.create(contentsStream, true, getMonitor());
+ }
+
+ private long getLastModificationTimestamp() {
+ IPath testFileLocation = testFile.getLocation();
+ String filePath = testFileLocation.toOSString();
+ FileInfo testFileInfo = LocalFileNativesManager.fetchFileInfo(filePath);
+ return testFileInfo.getLastModified();
+ }
+}

Back to the top