Skip to content

Commit 7b41467

Browse files
authored
Sync TrackingFileManager with 2.x (#1802)
Backport all the changes from 2.x to 1.x
1 parent a211ca9 commit 7b41467

File tree

1 file changed

+33
-4
lines changed

1 file changed

+33
-4
lines changed

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,28 @@ public boolean delete(File file) {
140140
return false;
141141
}
142142

143-
private Object mutex(Path path) {
143+
/**
144+
* This method creates a "mutex" object to synchronize on thread level, within same JVM, to prevent multiple
145+
* threads from trying to lock the same file at the same time. Threads concurrently working on different files
146+
* are okay, as after syncing on mutex, they operate with FS locking, that goal is to synchronize with possible
147+
* other Maven processes, and not with other threads in this JVM.
148+
*/
149+
private static Object mutex(Path path) {
144150
// The interned string of path is (mis)used as mutex, to exclude different threads going for same file,
145151
// as JVM file locking happens on JVM not on Thread level. This is how original code did it ¯\_(ツ)_/¯
146-
return path.toAbsolutePath().normalize().toString().intern();
152+
return canonicalPath(path).toString().intern();
153+
}
154+
155+
/**
156+
* Tries the best it can to figure out actual file the workload is about, while resolving cases like symlinked
157+
* local repository etc.
158+
*/
159+
private static Path canonicalPath(Path path) {
160+
try {
161+
return path.toRealPath();
162+
} catch (IOException e) {
163+
return canonicalPath(path.getParent()).resolve(path.getFileName());
164+
}
147165
}
148166

149167
private FileLock fileLock(FileChannel channel, boolean shared) throws IOException {
@@ -152,9 +170,20 @@ private FileLock fileLock(FileChannel channel, boolean shared) throws IOExceptio
152170
try {
153171
lock = channel.lock(0, Long.MAX_VALUE, shared);
154172
break;
155-
} catch (OverlappingFileLockException e) {
173+
} catch (OverlappingFileLockException | IOException e) {
174+
// For Unix process sun.nio.ch.UnixFileDispatcherImpl.lock0() is a native method that can throw
175+
// IOException with message "Resource deadlock avoided"
176+
// the system call level is involving fcntl() or flock()
177+
// If the kernel detects that granting the lock would result in a deadlock
178+
// (where two processes are waiting for each other to release locks which can happen when two processes
179+
// are trying to lock the same file),
180+
// it returns an EDEADLK error, which Java throws as an IOException.
181+
// Read another comment from
182+
// https://github.com/bdeployteam/bdeploy/blob/7c04e7228d6d48b8990e6703a8d476e21024c639/bhive/src/main/java/io/bdeploy/bhive/objects/LockableDatabase.java#L57
183+
// Note (cstamas): seems this MAY also happen where there is ONE process but performs locking on same
184+
// file from multiple threads, as Linux kernel performs lock detection on process level.
156185
if (attempts <= 0) {
157-
throw new IOException(e);
186+
throw (e instanceof IOException) ? (IOException) e : new IOException(e);
158187
}
159188
try {
160189
Thread.sleep(50L);

0 commit comments

Comments
 (0)