Skip to content

Add timed read acquisition and queue diagnostics to GraphLock - #294

Merged
mbastian merged 2 commits into
gephi:masterfrom
MattArtzAnthro:graphlock-timed-acquisition
Oct 25, 2026
Merged

mbastian merged 2 commits into
gephi:masterfrom
MattArtzAnthro:graphlock-timed-acquisition

Conversation

@MattArtzAnthro

@MattArtzAnthro MattArtzAnthro commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • GraphLock exposed only unbounded, non-interruptible readLock() / writeLock() calls, so a caller that cannot afford to wait indefinitely (a plugin doing graph work off the EDT while the viz engine renders) had no option in the public API short of reflecting into GraphLockImpl.
  • Adds tryReadLock(timeout, unit), delegating to the underlying ReentrantReadWriteLock's timed tryLock. The timed form enqueues rather than barging, which is why it did not starve in the reproducer on raphLock has no timed/try acquisition; polling writers can starve, and a stalled reader plus a queued writer can wedge all graph operations #282 where untimed tryLock() polling did (0 of 4637 attempts vs 2116 of 2116). A zero timeout returns immediately, and fails behind a queued writer unless the caller already holds the lock.
  • tryWriteLock is left out for now: a queued timed writer stalls every new reader for as long as it waits, and retrying with a short timeout can starve the writer. Its need is still to be assessed.
  • Adds getReadLockCount(), isWriteLocked(), and getQueueLength() for monitoring. getReadLockCount() counts holds across all threads, which is what makes a leaked read hold (an abandoned auto-locking iterator) diagnosable; today it is invisible to a thread dump once the holding thread has exited.
  • The four new methods are abstract, since GraphLockImpl is the only implementation in this repository. External implementations of GraphLock will need to implement them.
  • Javadoc on readLock() now states that a read hold across a cross-thread wait, or an iterator abandoned before exhaustion or doBreak(), blocks all graph operations once a writer queues. No existing behavior changes; fairness is untouched.

Part of #282

Test plan

  • Tests added to GraphLockImplTest: timed read acquire when free and after another thread's write is released; a queued writer blocks a new tryReadLock, but not a holder's; interruption during a timed wait propagates and leaves no waiter; getQueueLength reports a queued writer; getReadLockCount counts holds from two threads while getReadHoldCount stays per-thread; isWriteLocked follows lock and unlock. Contention is set up with latches, not sleeps, apart from a bounded poll until the contending thread is parked.
  • mvn test passes the full suite (1657 tests).

As with the issue, this was worked out together with Claude, Anthropic's AI assistant; I built and ran the tests on my machine.

@MattArtzAnthro
MattArtzAnthro force-pushed the graphlock-timed-acquisition branch 2 times, most recently from ca07fe7 to 890e9e7 Compare August 28, 2026 19:43
GraphLock exposed only unbounded, non-interruptible lock() calls, so a
caller that cannot afford to wait indefinitely had no option in the
public API. This adds tryReadLock and tryWriteLock with a timeout, plus
getReadLockCount, isWriteLocked, and getQueueLength for monitoring,
each delegating to the underlying ReentrantReadWriteLock. New methods
are default methods on the interface so existing implementations keep
compiling. Javadoc on readLock now states the consequence of holding a
read lock across a cross-thread wait or abandoning an auto-locking
iterator. Fixes gephi#282.
@MattArtzAnthro
MattArtzAnthro force-pushed the graphlock-timed-acquisition branch from 890e9e7 to 1ceca12 Compare September 25, 2026 13:39
@MattArtzAnthro
MattArtzAnthro requested a review from a team as a code owner September 25, 2026 13:39
- Drop tryWriteLock for now: a queued timed writer stalls every new reader for as long as it waits, and retrying with a short timeout can starve the writer. The need for it is yet to be assessed.
- Make the new GraphLock methods abstract, GraphLockImpl is the only implementation, so callers don't have to handle UnsupportedOperationException.
- Document that a zero timeout tryReadLock fails behind a queued writer only for threads that don't already hold the lock, that a pending interrupt throws on entry, that nothing may touch the graph after a failed attempt, and that isWriteLocked is for monitoring only.
- Test that a queued writer blocks a new tryReadLock, that an interrupted tryReadLock leaves no waiter, and that a holder's zero timeout tryReadLock still succeeds.
- Make the new tests wait for the thread to be parked and not leave threads behind on failure, as well as the queue length test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mbastian mbastian changed the title Add timed acquisition and queue diagnostics to GraphLock Add timed read acquisition and queue diagnostics to GraphLock Oct 25, 2026
@mbastian

Copy link
Copy Markdown
Member

Pushed d684bad on top: drops tryWriteLock for now (stalls new readers while queued, and short-timeout retries can starve the writer), makes the new methods abstract, and tightens the Javadoc and tests. Description updated to match.

@mbastian mbastian added this to the 0.8.11 milestone Oct 25, 2026
@mbastian
mbastian merged commit 08dc275 into gephi:master Oct 25, 2026
1 check passed
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.

2 participants