Skip to content

fix: fix LockManager deadlock detection not resolving cycles reliably#76

Draft
houssemexo26 wants to merge 2 commits into
developfrom
fixloc
Draft

fix: fix LockManager deadlock detection not resolving cycles reliably#76
houssemexo26 wants to merge 2 commits into
developfrom
fixloc

Conversation

@houssemexo26

@houssemexo26 houssemexo26 commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

The LockManager deadlock detector failed to identify actual deadlock cycles because checkDeadLockOnce() treated a null value from locks.get(currentOwner) as inconclusive in all cases. This caused the method to return false prematurely whenever the current owner thread was not registered as waiting. After exhausting its retry attempts, the detection logic exited without throwing an exception, leaving both threads permanently blocked.

The underlying issue was an incorrect interpretation of null. A null entry in the waiting map is only ambiguous at the initial step, where the direct lock owner might not yet have completed its register() and checkDeadLock() sequence. Beyond that first hop, however, null is definitive: it indicates the thread is not waiting on any lock and therefore cannot participate in a deadlock cycle.

All five TestLockManager tests failed consistently because isEmpty()
only tracked threads waiting to acquire a lock, not threads currently
holding one. Once a lock was acquired and unregister() was called, the
lock became invisible to isEmpty(), causing assertTrue(manager.isEmpty())
to fire while locks were still held.

Changes:
- Add heldLocks map to track locks that are currently held (acquired
  but not yet released). isEmpty() now checks both maps.
- Override unlock() in InternalReentrantLock to call unregisterHeld()
  when the hold count drops to zero (reentrant-safe).
- Call registerHeld() after successful acquisition in lock(),
  lockInterruptibly(), and tryLock() variants.
- Fix checkDeadLockOnce() returning false (inconclusive) when
  locks.get(owner) == null: a null entry means the owner is not
  waiting — this is a conclusive "no deadlock", not a race. Split
  the method into checkDeadLock() (spins only for the first hop)
  and walkDeadLockGraph() (definitive graph walk).
- Fix InternalFutureTask.run() which decremented totalUncompletedTasks
  without ever incrementing it. Replaced with registerHeld/unregisterHeld
  so running tasks are visible to isEmpty().
@houssemexo26
houssemexo26 marked this pull request as draft April 22, 2026 22:25
@houssemexo26 houssemexo26 changed the title fix: fix flaky TestLockManager tests by tracking held locks fix: fix LockManager deadlock detection not resolving cycles reliably Apr 23, 2026
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant