Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,19 @@ private FileLock fileLock(FileChannel channel, boolean shared) throws IOExceptio
try {
lock = channel.lock(0, Long.MAX_VALUE, shared);
break;
} catch (OverlappingFileLockException e) {
} catch (OverlappingFileLockException | IOException e) {
// For Unix process sun.nio.ch.UnixFileDispatcherImpl.lock0() is a native method that can throw
// IOException
// with message "Resource deadlock avoided"
// the system call level is involving fcntl() or flock()
// If the kernel detects that granting the lock would result in a deadlock
// (where two processes are waiting for each other to release locks which can happen when two processes
// are trying to lock the same file),
// it returns an EDEADLK error, which Java throws as an IOException.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even so, how would retrying help in this situation?

@olamy olamy Feb 19, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the wait time, the other process or thread will have released its lock. Bear in mind the os is doing so to prevent deadlock.
There is similar code in the Android https://github.com/androidx/androidx/blob/1c55c017b56b3d702e8013a0aa9baee866ea9fb7/datastore/datastore-core/src/androidMain/kotlin/androidx/datastore/core/MultiProcessCoordinator.android.kt#L77

and coursier
https://github.com/coursier/coursier/blob/345d58581abafac56891dcbaef24ec6e79ce0fb5/modules/bootstrap-launcher/src/main/java/coursier/bootstrap/launcher/Download.java#L208

as well as the comment added here.

Maybe we should limit to this if(e.getMessage().contains("Resource deadlock avoided")) though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this can only happen if the other process owning the lock is somehow waiting for this process. Otherwise why would it otherwise be detected as DEADLOCK situation in the first place? With the 2nd attempt how will the owning process be unblocked. IMHO retry only makes sense if some lock is released in between (the other process is waiting for). How would that be the case here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the message "Resource deadlock avoided" which means the deadlock has not happened yet
And this the key point, EDEADLK prevents the deadlock rather than reporting one that already exists.
When the kernel returns EDEADLK, it means he refused to grant the lock and returns immediately, the calling process is NOT blocked. The other process (which is blocked waiting) can now proceed because the cycle no longer exists. It completes its work, releases its locks, and when we retry after the 50ms sleep, the contention is resolved.
It looks this deadlock detection is known to eventually produce false positives [1] as it's a bit conservative. So the retry simply succeeds immediately.
Maybe adding e.getMessage().contains("Resource deadlock avoided") would be more precise, so we don't accidentally swallow unrelated IOExceptions during the lock attempt. I can add that filter.

Here the sequence:

  1. Process A locks tracking file X, then tries to lock tracking file Y
  2. Process B locks tracking file Y, then tries to lock tracking file X
  3. Kernel detects the circular dependency on Process B's attempt -> returns EDEADLK to B
  4. Process A is now unblocked (no circular wait) -> completes -> releases both locks
  5. Process B retries after 50ms -> succeeds

[1] https://man7.org/linux/man-pages/man2/F_SETLK.2const.html section Bugs Deadlock detection

// Read another comment from
// https://github.com/bdeployteam/bdeploy/blob/7c04e7228d6d48b8990e6703a8d476e21024c639/bhive/src/main/java/io/bdeploy/bhive/objects/LockableDatabase.java#L57
if (attempts <= 0) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add some "filtering" for IOException such?

if(e.getMessage().contains("Resource deadlock avoided"))

throw new IOException(e);
throw (e instanceof IOException) ? (IOException) e : new IOException(e);
}
try {
Thread.sleep(50L);
Expand Down
Loading