Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn2019-07-17 10:31:42 -0400
committerMatthias Sohn2019-08-06 08:54:39 -0400
commitd45219baacab711abf3c4112146ca0522d984be2 (patch)
tree2567ded44b9be10eee5f51a30e2f6094f0d7cbc7
parent5911521ba6d47868871c4b5f240c71af827b7aa2 (diff)
downloadjgit-d45219ba.tar.gz
jgit-d45219ba.tar.xz
jgit-d45219ba.zip
Persist minimal racy threshold and allow manual configuration
To enable persisting the minimal racy threshold per FileStore add a new config option to the user global git configuration: - Config section is "filesystem" - Config subsection is concatenation of - Java vendor (system property "java.vendor") - Java version (system property "java.version") - FileStore's name, on Windows we use the attribute volume:vsn instead since the name is not necessarily unique. - separated by '|' e.g. "AdoptOpenJDK|1.8.0_212-b03|/dev/disk1s1" The same prefix is used as for filesystem timestamp resolution, so both values are stored in the same config section - The config key for minmal racy threshold is "minRacyThreshold" as a time value, supported time units are those supported by DefaultTypedConfigGetter#getTimeUnit - measure for 3 seconds to limit runtime which depends on hardware, OS and Java version being used If the minimal racy threshold is configured for a given FileStore the configured value is used instead of measuring it. When the minimal racy threshold was measured it is persisted in the user global git configuration. Rename FileStoreAttributeCache to FileStoreAttributes since this class is now declared public in order to enable exposing all attributes in one object. Example: [filesystem "AdoptOpenJDK|11.0.3|/dev/disk1s1"] timestampResolution = 7000 nanoseconds minRacyThreshold = 3440 microseconds Change-Id: I22195e488453aae8d011b0a8e3276fe3d99deaea Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Also-By: Marc Strapetz <marc.strapetz@syntevo.com>
-rw-r--r--org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java2
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java2
-rw-r--r--org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java2
-rw-r--r--org.eclipse.jgit/.settings/.api_filters22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java10
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java30
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java202
12 files changed, 195 insertions, 94 deletions
diff --git a/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java b/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
index 9f9d459a6c..8043d2b183 100644
--- a/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
+++ b/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
@@ -66,7 +66,7 @@ public class GitCloneTaskTest extends LocalDiskRepositoryTestCase {
@Before
public void before() throws IOException {
dest = createTempFile();
- FS.getFileStoreAttributeCache(dest.toPath().getParent());
+ FS.getFileStoreAttributes(dest.toPath().getParent());
project = new Project();
project.init();
enableLogging();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index fb8295fa4b..af23ad1e35 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -130,7 +130,7 @@ public abstract class LocalDiskRepositoryTestCase {
// measure timer resolution before the test to avoid time critical tests
// are affected by time needed for measurement
- FS.getFileStoreAttributeCache(tmp.toPath().getParent());
+ FS.getFileStoreAttributes(tmp.toPath().getParent());
mockSystemReader = new MockSystemReader();
mockSystemReader.userGitConfig = new FileBasedConfig(new File(tmp,
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index ebd13e4112..5aacbbadec 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -378,7 +378,7 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
tmp = File.createTempFile("fsTickTmpFile", null,
lastFile.getParentFile());
}
- long res = FS.getFileStoreAttributeCache(tmp.toPath())
+ long res = FS.getFileStoreAttributes(tmp.toPath())
.getFsTimestampResolution().toNanos();
long sleepTime = res / 10;
try {
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
index 92a6ec351f..ec44da4cac 100644
--- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
@@ -123,7 +123,7 @@ public abstract class LfsServerTest {
// measure timer resolution before the test to avoid time critical tests
// are affected by time needed for measurement
- FS.getFileStoreAttributeCache(tmp.getParent());
+ FS.getFileStoreAttributes(tmp.getParent());
server = new AppServer();
ServletContextHandler app = server.addContext("/lfs");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
index 012407f715..40af9e2a00 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
@@ -62,7 +62,7 @@ import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.FS.FileStoreAttributeCache;
+import org.eclipse.jgit.util.FS.FileStoreAttributes;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.Stats;
import org.eclipse.jgit.util.SystemReader;
@@ -79,7 +79,7 @@ public class FileSnapshotTest {
private Path trash;
- private FileStoreAttributeCache fsAttrCache;
+ private FileStoreAttributes fsAttrCache;
@Before
public void setUp() throws Exception {
@@ -87,7 +87,7 @@ public class FileSnapshotTest {
// measure timer resolution before the test to avoid time critical tests
// are affected by time needed for measurement
fsAttrCache = FS
- .getFileStoreAttributeCache(trash.getParent());
+ .getFileStoreAttributes(trash.getParent());
}
@Before
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
index 77f5febc17..1adddb5ea5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
@@ -83,7 +83,7 @@ public class FileBasedConfigTest {
@Before
public void setUp() throws Exception {
trash = Files.createTempDirectory("tmp_");
- FS.getFileStoreAttributeCache(trash.getParent());
+ FS.getFileStoreAttributes(trash.getParent());
}
@After
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
index 63e295ec83..2054e1efa8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
@@ -203,7 +203,7 @@ public class FSTest {
.ofPattern("uuuu-MMM-dd HH:mm:ss.nnnnnnnnn", Locale.ENGLISH)
.withZone(ZoneId.systemDefault());
Path dir = Files.createTempDirectory("probe-filesystem");
- Duration resolution = FS.getFileStoreAttributeCache(dir)
+ Duration resolution = FS.getFileStoreAttributes(dir)
.getFsTimestampResolution();
long resolutionNs = resolution.toNanos();
assertTrue(resolutionNs > 0);
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index a027caaf02..8277735e24 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -52,6 +52,12 @@
<filter id="1142947843">
<message_arguments>
<message_argument value="5.1.9"/>
+ <message_argument value="CONFIG_KEY_MIN_RACY_THRESHOLD"/>
+ </message_arguments>
+ </filter>
+ <filter id="1142947843">
+ <message_arguments>
+ <message_argument value="5.1.9"/>
<message_argument value="CONFIG_KEY_TIMESTAMP_RESOLUTION"/>
</message_arguments>
</filter>
@@ -72,6 +78,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/storage/file/FileBasedConfig.java" type="org.eclipse.jgit.storage.file.FileBasedConfig">
+ <filter id="1142947843">
+ <message_arguments>
+ <message_argument value="5.1.9"/>
+ <message_argument value="load(boolean)"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
<filter id="336658481">
<message_arguments>
@@ -174,7 +188,7 @@
<filter id="1142947843">
<message_arguments>
<message_argument value="5.1.9"/>
- <message_argument value="getFileStoreAttributeCache(Path)"/>
+ <message_argument value="getFileStoreAttributes(Path)"/>
</message_arguments>
</filter>
<filter id="1142947843">
@@ -192,7 +206,7 @@
<filter id="1142947843">
<message_arguments>
<message_argument value="5.1.9"/>
- <message_argument value="setAsyncfileStoreAttrCache(boolean)"/>
+ <message_argument value="setAsyncFileStoreAttributes(boolean)"/>
</message_arguments>
</filter>
<filter id="1142947843">
@@ -210,11 +224,11 @@
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$FileStoreAttributeCache">
+ <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$FileStoreAttributes">
<filter id="1142947843">
<message_arguments>
<message_argument value="5.1.9"/>
- <message_argument value="FileStoreAttributeCache"/>
+ <message_argument value="FileStoreAttributes"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
index aa9f1cc45b..e81a88451b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
@@ -43,7 +43,7 @@
package org.eclipse.jgit.internal.storage.file;
-import static org.eclipse.jgit.util.FS.FileStoreAttributeCache.FALLBACK_FILESTORE_ATTRIBUTES;
+import static org.eclipse.jgit.util.FS.FileStoreAttributes.FALLBACK_FILESTORE_ATTRIBUTES;
import java.io.File;
import java.io.IOException;
@@ -58,7 +58,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.FS.FileStoreAttributeCache;
+import org.eclipse.jgit.util.FS.FileStoreAttributes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -215,7 +215,7 @@ public class FileSnapshot {
private final long size;
/** measured FileStore attributes */
- private FileStoreAttributeCache fileStoreAttributeCache;
+ private FileStoreAttributes fileStoreAttributeCache;
/**
* Object that uniquely identifies the given file, or {@code
@@ -254,7 +254,7 @@ public class FileSnapshot {
this.file = file;
this.lastRead = Instant.now();
this.fileStoreAttributeCache = useConfig
- ? FS.getFileStoreAttributeCache(file.toPath().getParent())
+ ? FS.getFileStoreAttributes(file.toPath().getParent())
: FALLBACK_FILESTORE_ATTRIBUTES;
BasicFileAttributes fileAttributes = null;
try {
@@ -293,7 +293,7 @@ public class FileSnapshot {
this.file = null;
this.lastRead = read;
this.lastModified = modified;
- this.fileStoreAttributeCache = new FileStoreAttributeCache(
+ this.fileStoreAttributeCache = new FileStoreAttributes(
fsTimestampResolution);
this.size = size;
this.fileKey = fileKey;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 4f636d4553..82ccd7b034 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -444,4 +444,11 @@ public final class ConfigConstants {
* @since 5.1.9
*/
public static final String CONFIG_KEY_TIMESTAMP_RESOLUTION = "timestampResolution";
+
+ /**
+ * The "minRacyThreshold" key
+ *
+ * @since 5.1.9
+ */
+ public static final String CONFIG_KEY_MIN_RACY_THRESHOLD = "minRacyThreshold";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 3a41643e6e..633632dc01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -149,13 +149,37 @@ public class FileBasedConfig extends StoredConfig {
*/
@Override
public void load() throws IOException, ConfigInvalidException {
+ load(true);
+ }
+
+ /**
+ * Load the configuration as a Git text style configuration file.
+ * <p>
+ * If the file does not exist, this configuration is cleared, and thus
+ * behaves the same as though the file exists, but is empty.
+ *
+ * @param useFileSnapshotWithConfig
+ * if {@code true} use the FileSnapshot with config, otherwise
+ * use it without config
+ * @throws IOException
+ * if IO failed
+ * @throws ConfigInvalidException
+ * if config is invalid
+ * @since 5.1.9
+ */
+ public void load(boolean useFileSnapshotWithConfig)
+ throws IOException, ConfigInvalidException {
final int maxStaleRetries = 5;
int retries = 0;
while (true) {
final FileSnapshot oldSnapshot = snapshot;
- // don't use config in this snapshot to avoid endless recursion
- final FileSnapshot newSnapshot = FileSnapshot
- .saveNoConfig(getFile());
+ final FileSnapshot newSnapshot;
+ if (useFileSnapshotWithConfig) {
+ newSnapshot = FileSnapshot.save(getFile());
+ } else {
+ // don't use config in this snapshot to avoid endless recursion
+ newSnapshot = FileSnapshot.saveNoConfig(getFile());
+ }
try {
final byte[] in = IO.readFully(getFile());
final ObjectId newHash = hash(in);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 08dab3201d..16810e0dc3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -208,9 +208,9 @@ public abstract class FS {
*
* @since 5.1.9
*/
- public final static class FileStoreAttributeCache {
+ public final static class FileStoreAttributes {
- private static final Duration UNDEFINED_RESOLUTION = Duration
+ private static final Duration UNDEFINED_DURATION = Duration
.ofNanos(Long.MAX_VALUE);
/**
@@ -218,10 +218,10 @@ public abstract class FS {
* filesystem timestamp resolution. The last modified time granularity
* of FAT filesystems is 2 seconds.
*/
- public static final FileStoreAttributeCache FALLBACK_FILESTORE_ATTRIBUTES = new FileStoreAttributeCache(
+ public static final FileStoreAttributes FALLBACK_FILESTORE_ATTRIBUTES = new FileStoreAttributes(
Duration.ofMillis(2000));
- private static final Map<FileStore, FileStoreAttributeCache> attributeCache = new ConcurrentHashMap<>();
+ private static final Map<FileStore, FileStoreAttributes> attributeCache = new ConcurrentHashMap<>();
private static AtomicBoolean background = new AtomicBoolean();
@@ -239,22 +239,24 @@ public abstract class FS {
.ofMillis(10);
/**
+ * Get the FileStoreAttributes for the given FileStore
+ *
* @param path
* file residing in the FileStore to get attributes for
- * @return FileStoreAttributeCache entry for the given path.
+ * @return FileStoreAttributes for the given path.
*/
- public static FileStoreAttributeCache get(Path path) {
+ public static FileStoreAttributes get(Path path) {
path = path.toAbsolutePath();
Path dir = Files.isDirectory(path) ? path : path.getParent();
- return getFileAttributeCache(dir);
+ return getFileStoreAttributes(dir);
}
- private static FileStoreAttributeCache getFileAttributeCache(Path dir) {
+ private static FileStoreAttributes getFileStoreAttributes(Path dir) {
FileStore s;
try {
if (Files.exists(dir)) {
s = Files.getFileStore(dir);
- FileStoreAttributeCache c = attributeCache.get(s);
+ FileStoreAttributes c = attributeCache.get(s);
if (c != null) {
return c;
}
@@ -272,7 +274,8 @@ public abstract class FS {
Thread.currentThread(), dir);
return FALLBACK_FILESTORE_ATTRIBUTES;
}
- CompletableFuture<Optional<FileStoreAttributeCache>> f = CompletableFuture
+
+ CompletableFuture<Optional<FileStoreAttributes>> f = CompletableFuture
.supplyAsync(() -> {
Lock lock = locks.computeIfAbsent(s,
l -> new ReentrantLock());
@@ -282,21 +285,27 @@ public abstract class FS {
Thread.currentThread(), dir);
return Optional.empty();
}
- Optional<FileStoreAttributeCache> cache = Optional
+ Optional<FileStoreAttributes> attributes = Optional
.empty();
try {
// Some earlier future might have set the value
// and removed itself since we checked for the
// value above. Hence check cache again.
- FileStoreAttributeCache c = attributeCache
+ FileStoreAttributes c = attributeCache
.get(s);
if (c != null) {
return Optional.of(c);
}
+ attributes = readFromConfig(s);
+ if (attributes.isPresent()) {
+ attributeCache.put(s, attributes.get());
+ return attributes;
+ }
+
Optional<Duration> resolution = measureFsTimestampResolution(
s, dir);
if (resolution.isPresent()) {
- c = new FileStoreAttributeCache(
+ c = new FileStoreAttributes(
resolution.get());
attributeCache.put(s, c);
// for high timestamp resolution measure
@@ -304,24 +313,28 @@ public abstract class FS {
if (c.fsTimestampResolution
.toNanos() < 100_000_000L) {
c.minimalRacyInterval = measureMinimalRacyInterval(
- dir);
+ dir);
}
if (LOG.isDebugEnabled()) {
LOG.debug(c.toString());
}
- cache = Optional.of(c);
+ saveToConfig(s, c);
}
+ attributes = Optional.of(c);
} finally {
lock.unlock();
locks.remove(s);
}
- return cache;
+ return attributes;
});
+ f.exceptionally(e -> {
+ LOG.error(e.getLocalizedMessage(), e);
+ return Optional.empty();
+ });
// even if measuring in background wait a little - if the result
// arrives, it's better than returning the large fallback
- Optional<FileStoreAttributeCache> d = f.get(
- background.get() ? 100 : 5000,
- TimeUnit.MILLISECONDS);
+ Optional<FileStoreAttributes> d = background.get() ? f.get(
+ 100, TimeUnit.MILLISECONDS) : f.get();
if (d.isPresent()) {
return d.get();
}
@@ -341,14 +354,16 @@ public abstract class FS {
private static Duration measureMinimalRacyInterval(Path dir) {
LOG.debug("{}: start measure minimal racy interval in {}", //$NON-NLS-1$
Thread.currentThread(), dir);
+ int n = 0;
int failures = 0;
long racyNanos = 0;
- final int COUNT = 1000;
ArrayList<Long> deltas = new ArrayList<>();
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
+ Instant end = Instant.now().plusSeconds(3);
try {
Files.createFile(probe);
- for (int i = 0; i < COUNT; i++) {
+ do {
+ n++;
write(probe, "a"); //$NON-NLS-1$
FileSnapshot snapshot = FileSnapshot.save(probe.toFile());
read(probe);
@@ -358,7 +373,7 @@ public abstract class FS {
racyNanos = snapshot.lastRacyThreshold();
failures++;
}
- }
+ } while (Instant.now().compareTo(end) < 0);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
return FALLBACK_MIN_RACY_INTERVAL;
@@ -376,7 +391,7 @@ public abstract class FS {
+ " delta max [ns], delta avg [ns]," //$NON-NLS-1$
+ " delta stddev [ns]\n" //$NON-NLS-1$
+ "{}, {}, {}, {}, {}, {}, {}", //$NON-NLS-1$
- COUNT, failures, racyNanos, stats.min(), stats.max(),
+ n, failures, racyNanos, stats.min(), stats.max(),
stats.avg(), stats.stddev());
return Duration
.ofNanos(Double.valueOf(stats.max()).longValue());
@@ -405,10 +420,6 @@ public abstract class FS {
FileStore s, Path dir) {
LOG.debug("{}: start measure timestamp resolution {} in {}", //$NON-NLS-1$
Thread.currentThread(), s, dir);
- Duration configured = readFileTimeResolution(s);
- if (!UNDEFINED_RESOLUTION.equals(configured)) {
- return Optional.of(configured);
- }
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
try {
Files.createFile(probe);
@@ -423,7 +434,6 @@ public abstract class FS {
Duration fsResolution = Duration.between(t1.toInstant(), t2.toInstant());
Duration clockResolution = measureClockResolution();
fsResolution = fsResolution.plus(clockResolution);
- saveFileTimeResolution(s, fsResolution);
LOG.debug("{}: end measure timestamp resolution {} in {}", //$NON-NLS-1$
Thread.currentThread(), s, dir);
return Optional.of(fsResolution);
@@ -454,20 +464,20 @@ public abstract class FS {
}
private static void deleteProbe(Path probe) {
- if (Files.exists(probe)) {
- try {
- Files.delete(probe);
- } catch (IOException e) {
- LOG.error(e.getLocalizedMessage(), e);
- }
+ try {
+ FileUtils.delete(probe.toFile(),
+ FileUtils.SKIP_MISSING | FileUtils.RETRY);
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
}
}
- private static Duration readFileTimeResolution(FileStore s) {
+ private static Optional<FileStoreAttributes> readFromConfig(
+ FileStore s) {
FileBasedConfig userConfig = SystemReader.getInstance()
.openUserConfig(null, FS.DETECTED);
try {
- userConfig.load();
+ userConfig.load(false);
} catch (IOException e) {
LOG.error(MessageFormat.format(JGitText.get().readConfigFailed,
userConfig.getFile().getAbsolutePath()), e);
@@ -477,49 +487,65 @@ public abstract class FS {
userConfig.getFile().getAbsolutePath(),
e.getMessage()));
}
- Duration configured = Duration
- .ofNanos(userConfig.getTimeUnit(
- ConfigConstants.CONFIG_FILESYSTEM_SECTION,
- javaVersionPrefix + s.name(),
- ConfigConstants.CONFIG_KEY_TIMESTAMP_RESOLUTION,
- UNDEFINED_RESOLUTION.toNanos(),
- TimeUnit.NANOSECONDS));
- return configured;
+ String key = getConfigKey(s);
+ Duration resolution = Duration.ofNanos(userConfig.getTimeUnit(
+ ConfigConstants.CONFIG_FILESYSTEM_SECTION, key,
+ ConfigConstants.CONFIG_KEY_TIMESTAMP_RESOLUTION,
+ UNDEFINED_DURATION.toNanos(), TimeUnit.NANOSECONDS));
+ if (UNDEFINED_DURATION.equals(resolution)) {
+ return Optional.empty();
+ }
+ Duration minRacyThreshold = Duration.ofNanos(userConfig.getTimeUnit(
+ ConfigConstants.CONFIG_FILESYSTEM_SECTION, key,
+ ConfigConstants.CONFIG_KEY_MIN_RACY_THRESHOLD,
+ UNDEFINED_DURATION.toNanos(), TimeUnit.NANOSECONDS));
+ FileStoreAttributes c = new FileStoreAttributes(resolution);
+ if (!UNDEFINED_DURATION.equals(minRacyThreshold)) {
+ c.minimalRacyInterval = minRacyThreshold;
+ }
+ return Optional.of(c);
}
- private static void saveFileTimeResolution(FileStore s,
- Duration resolution) {
+ private static void saveToConfig(FileStore s,
+ FileStoreAttributes c) {
FileBasedConfig userConfig = SystemReader.getInstance()
.openUserConfig(null, FS.DETECTED);
- long nanos = resolution.toNanos();
- TimeUnit unit;
- if (nanos < 200_000L) {
- unit = TimeUnit.NANOSECONDS;
- } else if (nanos < 200_000_000L) {
- unit = TimeUnit.MICROSECONDS;
- } else {
- unit = TimeUnit.MILLISECONDS;
- }
+ long resolution = c.getFsTimestampResolution().toNanos();
+ TimeUnit resolutionUnit = getUnit(resolution);
+ long resolutionValue = resolutionUnit.convert(resolution,
+ TimeUnit.NANOSECONDS);
+
+ long minRacyThreshold = c.getMinimalRacyInterval().toNanos();
+ TimeUnit minRacyThresholdUnit = getUnit(minRacyThreshold);
+ long minRacyThresholdValue = minRacyThresholdUnit
+ .convert(minRacyThreshold, TimeUnit.NANOSECONDS);
final int max_retries = 5;
int retries = 0;
boolean succeeded = false;
- long value = unit.convert(nanos, TimeUnit.NANOSECONDS);
+ String key = getConfigKey(s);
while (!succeeded && retries < max_retries) {
try {
- userConfig.load();
+ userConfig.load(false);
userConfig.setString(
- ConfigConstants.CONFIG_FILESYSTEM_SECTION,
- javaVersionPrefix + s.name(),
+ ConfigConstants.CONFIG_FILESYSTEM_SECTION, key,
ConfigConstants.CONFIG_KEY_TIMESTAMP_RESOLUTION,
String.format("%d %s", //$NON-NLS-1$
- Long.valueOf(value),
- unit.name().toLowerCase()));
+ Long.valueOf(resolutionValue),
+ resolutionUnit.name().toLowerCase()));
+ userConfig.setString(
+ ConfigConstants.CONFIG_FILESYSTEM_SECTION, key,
+ ConfigConstants.CONFIG_KEY_MIN_RACY_THRESHOLD,
+ String.format("%d %s", //$NON-NLS-1$
+ Long.valueOf(minRacyThresholdValue),
+ minRacyThresholdUnit.name().toLowerCase()));
userConfig.save();
succeeded = true;
} catch (LockFailedException e) {
// race with another thread, wait a bit and try again
try {
+ LOG.warn(MessageFormat.format(JGitText.get().cannotLock,
+ userConfig.getFile().getAbsolutePath()));
retries++;
Thread.sleep(20);
} catch (InterruptedException e1) {
@@ -538,6 +564,38 @@ public abstract class FS {
}
}
+ private static String getConfigKey(FileStore s) {
+ final String storeKey;
+ if (SystemReader.getInstance().isWindows()) {
+ Object attribute = null;
+ try {
+ attribute = s.getAttribute("volume:vsn"); //$NON-NLS-1$
+ } catch (IOException ignored) {
+ // ignore
+ }
+ if (attribute instanceof Integer) {
+ storeKey = attribute.toString();
+ } else {
+ storeKey = s.name();
+ }
+ } else {
+ storeKey = s.name();
+ }
+ return javaVersionPrefix + storeKey;
+ }
+
+ private static TimeUnit getUnit(long nanos) {
+ TimeUnit unit;
+ if (nanos < 200_000L) {
+ unit = TimeUnit.NANOSECONDS;
+ } else if (nanos < 200_000_000L) {
+ unit = TimeUnit.MICROSECONDS;
+ } else {
+ unit = TimeUnit.MILLISECONDS;
+ }
+ return unit;
+ }
+
private final @NonNull Duration fsTimestampResolution;
private Duration minimalRacyInterval;
@@ -565,7 +623,7 @@ public abstract class FS {
*
* @param fsTimestampResolution
*/
- public FileStoreAttributeCache(
+ public FileStoreAttributes(
@NonNull Duration fsTimestampResolution) {
this.fsTimestampResolution = fsTimestampResolution;
this.minimalRacyInterval = Duration.ZERO;
@@ -575,7 +633,7 @@ public abstract class FS {
@Override
public String toString() {
return String.format(
- "FileStoreAttributeCache[fsTimestampResolution=%,d µs, "
+ "FileStoreAttributes[fsTimestampResolution=%,d µs, "
+ "minimalRacyInterval=%,d µs]",
fsTimestampResolution.toNanos() / 1000,
minimalRacyInterval.toNanos() / 1000);
@@ -598,17 +656,16 @@ public abstract class FS {
}
/**
- * Whether FileStore attribute cache entries should be determined
- * asynchronously
+ * Whether FileStore attributes should be determined asynchronously
*
* @param asynch
- * whether FileStore attribute cache entries should be determined
+ * whether FileStore attributes should be determined
* asynchronously. If false access to cached attributes may block
* for some seconds for the first call per FileStore
* @since 5.1.9
*/
- public static void setAsyncfileStoreAttrCache(boolean asynch) {
- FileStoreAttributeCache.setBackground(asynch);
+ public static void setAsyncFileStoreAttributes(boolean asynch) {
+ FileStoreAttributes.setBackground(asynch);
}
/**
@@ -639,9 +696,8 @@ public abstract class FS {
}
/**
- * Get an estimate for the filesystem timestamp resolution from a cache of
- * timestamp resolution per FileStore, if not yet available it is measured
- * for a probe file under the given directory.
+ * Get cached FileStore attributes, if not yet available measure them using
+ * a probe file under the given directory.
*
* @param dir
* the directory under which the probe file will be created to
@@ -649,9 +705,9 @@ public abstract class FS {
* @return measured filesystem timestamp resolution
* @since 5.1.9
*/
- public static FileStoreAttributeCache getFileStoreAttributeCache(
+ public static FileStoreAttributes getFileStoreAttributes(
@NonNull Path dir) {
- return FileStoreAttributeCache.get(dir);
+ return FileStoreAttributes.get(dir);
}
private volatile Holder<File> userHome;

Back to the top