@@ -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