Skip to content

Commit 4e7a189

Browse files
committed
[bugfix] Avoid deadlock with NativeBroker#getXMLResource(XmldbURI, LockMode) when <document use-path-locks=true/> is set in conf.xml
1 parent b3f4bfe commit 4e7a189

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

exist-core/src/main/java/org/exist/storage/NativeBroker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2209,7 +2209,8 @@ public LockedDocument getXMLResource(XmldbURI fileName, final LockMode lockMode)
22092209
//TODO : resolve URIs !
22102210
final XmldbURI collUri = fileName.removeLastSegment();
22112211
final XmldbURI docUri = fileName.lastSegment();
2212-
try(final Collection collection = openCollection(collUri, LockMode.READ_LOCK)) {
2212+
final LockMode collectionLockMode = lockManager.relativeCollectionLockMode(LockMode.READ_LOCK, lockMode);
2213+
try(final Collection collection = openCollection(collUri, collectionLockMode)) {
22132214
if (collection == null) {
22142215
LOG.debug("Collection '{}' not found!", collUri);
22152216
return null;

exist-core/src/main/java/org/exist/storage/lock/LockManager.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,48 @@ public boolean isDocumentLockedForRead(final XmldbURI documentPath) {
602602
return existingLock.getReadLockCount() > 0;
603603
}
604604

605+
/**
606+
* Returns the LockMode that should be used for accessing
607+
* a Collection when that access is just for the purposes
608+
* or accessing a document(s) within the Collection.
609+
*
610+
* When Path Locks are enabled for Documents, both Collections and
611+
* Documents share the same lock domain, a path based tree hierarchy.
612+
* With a shared lock-hierarchy, if we were to READ_LOCK a Collection
613+
* and then request a WRITE_LOCK for a Document in that Collection we
614+
* would reach a dead-lock situation. To avoid this, if this method is
615+
* called like
616+
* {@code relativeCollectionLockMode(LockMode.READ_LOCK, LockMode.WRITE_LOCK)}
617+
* it will return a WRITE_LOCK. That is to say that to aid dealock-avoidance,
618+
* this function may return a stricter locking mode than the {@code desiredCollectionLockMode}.
619+
*
620+
* When Path Locks are disabled (the default) for Documents, Collection and Documents
621+
* have independent locking domains. In this case this function will always return
622+
* the {@code desiredCollectionLockMode}.
623+
*
624+
* @param desiredCollectionLockMode The desired lock mode for the Collection.
625+
* @param documentLockMode The lock mode that will be used for subsequent document operations in the Collection.
626+
*
627+
* @return The lock mode that should be used for accessing the Collection.
628+
*/
629+
public Lock.LockMode relativeCollectionLockMode(final Lock.LockMode desiredCollectionLockMode,
630+
final Lock.LockMode documentLockMode) {
631+
if (!usePathLocksForDocuments) {
632+
return desiredCollectionLockMode;
633+
634+
} else {
635+
switch (documentLockMode) {
636+
case NO_LOCK:
637+
case INTENTION_READ:
638+
case READ_LOCK:
639+
return Lock.LockMode.READ_LOCK;
640+
641+
default:
642+
return Lock.LockMode.WRITE_LOCK;
643+
}
644+
}
645+
}
646+
605647
/**
606648
* Retrieves a lock for a {@link org.exist.storage.dom.DOMFile}
607649
*

exist-core/src/main/java/org/exist/xmlrpc/RpcConnection.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,11 @@ protected LockedDocumentMap beginProtected(final DBBroker broker, final Map<Stri
275275
do {
276276
MutableDocumentSet docs = null;
277277
final LockedDocumentMap lockedDocuments = new LockedDocumentMap();
278-
try(final Collection coll = broker.openCollection(XmldbURI.createInternal(protectColl), LockMode.READ_LOCK)) {
278+
final LockMode documentLockMode = LockMode.WRITE_LOCK;
279+
final LockMode collectionLockMode = broker.getBrokerPool().getLockManager().relativeCollectionLockMode(LockMode.READ_LOCK, documentLockMode);
280+
try (final Collection coll = broker.openCollection(XmldbURI.createInternal(protectColl), collectionLockMode)) {
279281
docs = new DefaultDocumentSet();
280-
coll.allDocs(broker, docs, true, lockedDocuments, LockMode.WRITE_LOCK);
282+
coll.allDocs(broker, docs, true, lockedDocuments, documentLockMode);
281283
return lockedDocuments;
282284
} catch (final LockException e) {
283285
LOG.warn("Deadlock detected. Starting over again. Docs: {}; locked: {}. Cause: {}", docs.getDocumentCount(), lockedDocuments.size(), e.getMessage());

0 commit comments

Comments
 (0)