oracle/graal · error · LockedException

Document is locked by {}

Error message

Document is locked by {}

What it means

GraphDocument.writeLock(operationLabel, cancel) grants a single non-reentrant exclusive lock for document modification. If a DocumentLock is already held (lock != null), a second writeLock call throws LockedException naming the current holder, so concurrent or nested writers cannot corrupt document state.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graphio/parsing/model/GraphDocument.java:323

        public String getLockingOperation() {
            return theLock.getOperationLabel();
        }

        public boolean tryBreakLock() {
            return theLock.cancel();
        }
    }

    /**
     * Locks document for writing for one operation. The lock is NOT reentrant.
     *
     * @param operationLabel user-readable label of the lock owner.
     * @param cancel optional; callback to cancel the operation
     * @return lock instance
     */
    public synchronized DocumentLock writeLock(String operationLabel, Callable<Boolean> cancel) {
        if (lock != null) {
            throw new LockedException("Document is locked by " + lock.getOperationLabel(), lock);
        }
        return lock = new DocumentLock(operationLabel, cancel);
    }

    /**
     * Represents a lock on the document. During the lock, no other modifications are permitted
     * (throw a {@link LockedException}). Lock owner may disable modification tracking, i.e. during
     * load from file, the GraphDocument does not become modified.
     * <p>
     * Modification tracking is restored when the lock is released.
     */
    public final class DocumentLock implements AutoCloseable {
        private final String operationLabel;
        private final Callable<Boolean> cancelFunc;
        private boolean unlocked;

        public DocumentLock(String operationLabel, Callable<Boolean> cancelFunc) {
            this.operationLabel = operationLabel;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Wrap every DocumentLock in try-with-resources (it is AutoCloseable) so it is always released
  2. Pass the existing DocumentLock down into nested code instead of calling writeLock again; restructure so locking happens at one level
  3. Before locking, check/await the current lock (catch LockedException or inspect the document state) and retry when the owning operation finishes

Example fix

// before
try (DocumentLock l = doc.writeLock("load", null)) {
    applyEdits(doc); // internally calls doc.writeLock("edit") -> LockedException
}

// after: pass the lock, don't re-lock
try (DocumentLock l = doc.writeLock("load", null)) {
    applyEdits(doc, l);
}
Defensive patterns

Strategy: try-catch

Validate before calling

synchronized (doc) { /* can only peek whether a lock exists via attempting writeLock in a try/catch; no public isLocked() — track your own lock scope */ }

Try / catch

try (DocumentLock l = doc.writeLock(label, cancel)) { ... }
catch (LockedException e) { /* e.getMessage() names the holder; queue the edit or retry after the operation completes */ }

Prevention

When it happens

Trigger: Calling document.writeLock(...) while another operation holds the lock (e.g. a file load still in progress, or a nested writeLock inside code already running under a lock). The lock is explicitly NOT reentrant, so even the same thread re-locking throws.

Common situations: A background loader parsing a graph file while the UI (or another tool) tries to apply edits; nesting writeLock in helper methods that are themselves called under a lock; forgetting to close() a DocumentLock in a finally block so it stays held forever.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/534c4c8dabb9d698. Report an issue: GitHub.