Skip to content

Commit 1d938c3

Browse files
authored
Merge pull request #4765 from evolvedbinary/feature/lock-object-optimisation
Lock object optimisation
2 parents c0ebe54 + 27fac6f commit 1d938c3

File tree

10 files changed

+282
-70
lines changed

10 files changed

+282
-70
lines changed

exist-core/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,12 +710,16 @@
710710
<exclude>src/main/java/org/exist/storage/lock/LockEventJsonListener.java</exclude>
711711
<exclude>src/main/java/org/exist/storage/lock/LockEventLogListener.java</exclude>
712712
<exclude>src/main/java/org/exist/storage/lock/LockEventXmlListener.java</exclude>
713+
<exclude>src/main/java/org/exist/storage/lock/LockedPath.java</exclude>
714+
<exclude>src/main/java/org/exist/storage/lock/LockGroup.java</exclude>
713715
<exclude>src/main/java/org/exist/storage/lock/LockManager.java</exclude>
714716
<exclude>src/main/java/org/exist/storage/lock/LockTable.java</exclude>
715717
<exclude>src/main/java/org/exist/storage/lock/LockTableUtils.java</exclude>
716718
<exclude>src/main/java/org/exist/storage/lock/ManagedCollectionLock.java</exclude>
717719
<exclude>src/main/java/org/exist/storage/lock/ManagedDocumentLock.java</exclude>
718720
<exclude>src/main/java/org/exist/storage/lock/ManagedLock.java</exclude>
721+
<exclude>src/main/java/org/exist/storage/lock/ManagedLockGroupDocumentLock.java</exclude>
722+
<exclude>src/main/java/org/exist/storage/lock/ManagedSingleLockDocumentLock.java</exclude>
719723
<exclude>src/test/java/org/exist/storage/lock/CollectionLocksTest.java</exclude>
720724
<exclude>src/test/java/org/exist/storage/lock/DocumentLocksTest.java</exclude>
721725
<exclude>src/test/java/org/exist/storage/lock/LockManagerTest.java</exclude>
@@ -862,12 +866,16 @@ The original license statement is also included below.]]></preamble>
862866
<include>src/main/java/org/exist/storage/lock/LockEventJsonListener.java</include>
863867
<include>src/main/java/org/exist/storage/lock/LockEventLogListener.java</include>
864868
<include>src/main/java/org/exist/storage/lock/LockEventXmlListener.java</include>
869+
<include>src/main/java/org/exist/storage/lock/LockedPath.java</include>
870+
<include>src/main/java/org/exist/storage/lock/LockGroup.java</include>
865871
<include>src/main/java/org/exist/storage/lock/LockManager.java</include>
866872
<include>src/main/java/org/exist/storage/lock/LockTable.java</include>
867873
<include>src/main/java/org/exist/storage/lock/LockTableUtils.java</include>
868874
<include>src/main/java/org/exist/storage/lock/ManagedCollectionLock.java</include>
869875
<include>src/main/java/org/exist/storage/lock/ManagedDocumentLock.java</include>
870876
<include>src/main/java/org/exist/storage/lock/ManagedLock.java</include>
877+
<include>src/main/java/org/exist/storage/lock/ManagedLockGroupDocumentLock.java</include>
878+
<include>src/main/java/org/exist/storage/lock/ManagedSingleLockDocumentLock.java</include>
871879
<include>src/test/java/org/exist/storage/lock/CollectionLocksTest.java</include>
872880
<include>src/test/java/org/exist/storage/lock/DocumentLocksTest.java</include>
873881
<include>src/test/java/org/exist/storage/lock/LockManagerTest.java</include>

exist-core/src/main/java/org/exist/collections/MutableCollection.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ private void addDocumentsToSet(final DBBroker broker, final Iterator<DocumentImp
568568

569569
case NO_LOCK:
570570
default:
571-
documentLock = ManagedDocumentLock.notLocked(doc.getURI());
571+
documentLock = ManagedSingleLockDocumentLock.notLocked(doc.getURI());
572572
break;
573573
}
574574

@@ -710,7 +710,7 @@ public LockedDocument getDocumentWithLock(final DBBroker broker, final XmldbURI
710710

711711
case NO_LOCK:
712712
default:
713-
documentLock = ManagedDocumentLock.notLocked(getURI().append(name.lastSegment()));
713+
documentLock = ManagedSingleLockDocumentLock.notLocked(getURI().append(name.lastSegment()));
714714
unlockFn = () -> {};
715715
}
716716

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to eXist-db by
5+
* Evolved Binary, for the benefit of the eXist-db Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to eXist-db, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in eXist-db.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* ---------------------------------------------------------------------
17+
*
18+
* Copyright (C) 2014, Evolved Binary Ltd
19+
*
20+
* This library is free software; you can redistribute it and/or
21+
* modify it under the terms of the GNU Lesser General Public
22+
* License as published by the Free Software Foundation; version 2.1.
23+
*
24+
* This library is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27+
* Lesser General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU Lesser General Public
30+
* License along with this library; if not, write to the Free Software
31+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32+
*/
33+
package org.exist.storage.lock;
34+
35+
/**
36+
* @author <a href="mailto:[email protected]">Adam Retter</a>
37+
*/
38+
class LockGroup {
39+
final long groupId;
40+
final LockedPath[] locks;
41+
42+
43+
LockGroup(final long groupId, final LockedPath[] locks) {
44+
this.groupId = groupId;
45+
this.locks = locks;
46+
}
47+
}

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

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
*/
3333
package org.exist.storage.lock;
3434

35-
import com.evolvedbinary.j8fu.tuple.Tuple3;
3635
import org.apache.logging.log4j.LogManager;
3736
import org.apache.logging.log4j.Logger;
3837
import org.exist.storage.lock.Lock.LockType;
@@ -42,7 +41,6 @@
4241
import org.exist.xmldb.XmldbURI;
4342
import uk.ac.ic.doc.slurp.multilock.MultiLock;
4443

45-
import java.util.Arrays;
4644
import java.util.concurrent.locks.ReentrantLock;
4745
import java.util.function.Consumer;
4846

@@ -219,19 +217,8 @@ public ManagedCollectionLock acquireCollectionReadLock(final XmldbURI collection
219217
final LockGroup lockGroup = acquirePathReadLock(LockType.COLLECTION, collectionPath);
220218
return new ManagedCollectionLock(
221219
collectionPath,
222-
Arrays.stream(lockGroup.locks).map(Tuple3::get_1).toArray(MultiLock[]::new),
223-
() -> unlockAll(lockGroup.locks, l -> lockTable.released(lockGroup.groupId, l._3, LockType.COLLECTION, l._2))
224-
);
225-
}
226-
227-
private static class LockGroup {
228-
final long groupId;
229-
final Tuple3<MultiLock, Lock.LockMode, String>[] locks;
230-
231-
private LockGroup(final long groupId, final Tuple3<MultiLock, Lock.LockMode, String>[] locks) {
232-
this.groupId = groupId;
233-
this.locks = locks;
234-
}
220+
lockGroup,
221+
lockTable);
235222
}
236223

237224
/**
@@ -250,7 +237,7 @@ public LockGroup acquirePathReadLock(final LockType lockType, final XmldbURI pat
250237
final long groupId = System.nanoTime();
251238

252239
String pathStr = "";
253-
final Tuple3<MultiLock, Lock.LockMode, String>[] locked = new Tuple3[segments.length];
240+
final LockedPath[] locked = new LockedPath[segments.length];
254241
for (int i = 0; i < segments.length; i++) {
255242
pathStr += '/' + segments[i].toString();
256243

@@ -265,12 +252,12 @@ public LockGroup acquirePathReadLock(final LockType lockType, final XmldbURI pat
265252

266253
lockTable.attempt(groupId, pathStr, lockType, lockMode);
267254
if (lock(lock, lockMode)) {
268-
locked[i] = new Tuple3<>(lock, lockMode, pathStr);
255+
locked[i] = new LockedPath(lock, lockMode, pathStr);
269256
lockTable.acquired(groupId, pathStr, lockType, lockMode);
270257
} else {
271258
lockTable.attemptFailed(groupId, pathStr, lockType, lockMode);
272259

273-
unlockAll(locked, l -> lockTable.released(groupId, l._3, lockType, l._2));
260+
unlockAll(locked, l -> lockTable.released(groupId, l.path, lockType, l.mode));
274261

275262
throw new LockException("Unable to acquire " + lockType + " " + lockMode + " for: " + pathStr);
276263
}
@@ -287,7 +274,7 @@ public LockGroup acquirePathReadLock(final LockType lockType, final XmldbURI pat
287274
*
288275
* @return true, if we were able to lock with the mode.
289276
*/
290-
private boolean lock(final MultiLock lock, final Lock.LockMode lockMode) {
277+
private static boolean lock(final MultiLock lock, final Lock.LockMode lockMode) {
291278
switch (lockMode) {
292279
case INTENTION_READ:
293280
lock.intentionReadLock();
@@ -319,10 +306,10 @@ private boolean lock(final MultiLock lock, final Lock.LockMode lockMode) {
319306
*
320307
* @param locked An array of locks in acquisition order
321308
*/
322-
private void unlockAll(final Tuple3<MultiLock, Lock.LockMode, String>[] locked, final Consumer<Tuple3<MultiLock, Lock.LockMode, String>> unlockListener) {
323-
for(int i = locked.length - 1; i >= 0; i--) {
324-
final Tuple3<MultiLock, Lock.LockMode, String> lock = locked[i];
325-
unlock(lock._1, lock._2);
309+
static void unlockAll(final LockedPath[] locked, final Consumer<LockedPath> unlockListener) {
310+
for (int i = locked.length - 1; i >= 0; i--) {
311+
final LockedPath lock = locked[i];
312+
unlock(lock.lock, lock.mode);
326313
unlockListener.accept(lock);
327314
}
328315
}
@@ -333,8 +320,8 @@ private void unlockAll(final Tuple3<MultiLock, Lock.LockMode, String>[] locked,
333320
* @param lock The lock object to unlock.
334321
* @param lockMode The mode of the {@code lock} to release.
335322
*/
336-
private void unlock(final MultiLock lock, final Lock.LockMode lockMode) {
337-
switch(lockMode) {
323+
static void unlock(final MultiLock lock, final Lock.LockMode lockMode) {
324+
switch (lockMode) {
338325
case INTENTION_READ:
339326
lock.unlockIntentionRead();
340327
break;
@@ -380,9 +367,8 @@ ManagedCollectionLock acquireCollectionWriteLock(final XmldbURI collectionPath,
380367
final LockGroup lockGroup = acquirePathWriteLock(LockType.COLLECTION, collectionPath, lockParent);
381368
return new ManagedCollectionLock(
382369
collectionPath,
383-
Arrays.stream(lockGroup.locks).map(Tuple3::get_1).toArray(MultiLock[]::new),
384-
() -> unlockAll(lockGroup.locks, l -> lockTable.released(lockGroup.groupId, l._3, LockType.COLLECTION, l._2))
385-
);
370+
lockGroup,
371+
lockTable);
386372
}
387373

388374
/**
@@ -400,7 +386,7 @@ LockGroup acquirePathWriteLock(final LockType lockType, final XmldbURI path,
400386
final long groupId = System.nanoTime();
401387

402388
String pathStr = "";
403-
final Tuple3<MultiLock, Lock.LockMode, String>[] locked = new Tuple3[segments.length];
389+
final LockedPath[] locked = new LockedPath[segments.length];
404390
for (int i = 0; i < segments.length; i++) {
405391
pathStr += '/' + segments[i].toString();
406392

@@ -437,12 +423,12 @@ LockGroup acquirePathWriteLock(final LockType lockType, final XmldbURI path,
437423

438424
lockTable.attempt(groupId, pathStr, lockType, lockMode);
439425
if (lock(lock, lockMode)) {
440-
locked[i] = new Tuple3<>(lock, lockMode, pathStr);
426+
locked[i] = new LockedPath(lock, lockMode, pathStr);
441427
lockTable.acquired(groupId, pathStr, lockType, lockMode);
442428
} else {
443429
lockTable.attemptFailed(groupId, pathStr, lockType, lockMode);
444430

445-
unlockAll(locked, l -> lockTable.released(groupId, l._3, lockType, l._2));
431+
unlockAll(locked, l -> lockTable.released(groupId, l.path, lockType, l.mode));
446432

447433
throw new LockException("Unable to acquire " + lockType + " " + lockMode + " for: " + pathStr);
448434
}
@@ -503,11 +489,7 @@ MultiLock getDocumentLock(final String documentPath) {
503489
public ManagedDocumentLock acquireDocumentReadLock(final XmldbURI documentPath) throws LockException {
504490
if (usePathLocksForDocuments) {
505491
final LockGroup lockGroup = acquirePathReadLock(LockType.DOCUMENT, documentPath);
506-
return new ManagedDocumentLock(
507-
documentPath,
508-
Arrays.stream(lockGroup.locks).map(Tuple3::get_1).toArray(MultiLock[]::new),
509-
() -> unlockAll(lockGroup.locks, l -> lockTable.released(lockGroup.groupId, l._3, LockType.DOCUMENT, l._2))
510-
);
492+
return new ManagedLockGroupDocumentLock(documentPath, lockGroup, lockTable);
511493
} else {
512494
final long groupId = System.nanoTime();
513495
final String path = documentPath.toString();
@@ -523,10 +505,7 @@ public ManagedDocumentLock acquireDocumentReadLock(final XmldbURI documentPath)
523505
throw new LockException("Unable to acquire READ_LOCK for: " + path);
524506
}
525507

526-
return new ManagedDocumentLock(documentPath, lock, () -> {
527-
lock.asReadLock().unlock();
528-
lockTable.released(groupId, path, LockType.DOCUMENT, Lock.LockMode.READ_LOCK);
529-
});
508+
return new ManagedSingleLockDocumentLock(documentPath, groupId, lock, Lock.LockMode.READ_LOCK, lockTable);
530509
}
531510
}
532511

@@ -542,11 +521,7 @@ public ManagedDocumentLock acquireDocumentReadLock(final XmldbURI documentPath)
542521
public ManagedDocumentLock acquireDocumentWriteLock(final XmldbURI documentPath) throws LockException {
543522
if (usePathLocksForDocuments) {
544523
final LockGroup lockGroup = acquirePathWriteLock(LockType.DOCUMENT, documentPath, false);
545-
return new ManagedDocumentLock(
546-
documentPath,
547-
Arrays.stream(lockGroup.locks).map(Tuple3::get_1).toArray(MultiLock[]::new),
548-
() -> unlockAll(lockGroup.locks, l -> lockTable.released(lockGroup.groupId, l._3, LockType.DOCUMENT, l._2))
549-
);
524+
return new ManagedLockGroupDocumentLock(documentPath, lockGroup, lockTable);
550525
} else {
551526
final long groupId = System.nanoTime();
552527
final String path = documentPath.toString();
@@ -561,10 +536,7 @@ public ManagedDocumentLock acquireDocumentWriteLock(final XmldbURI documentPath)
561536
throw new LockException("Unable to acquire WRITE_LOCK for: " + path);
562537
}
563538

564-
return new ManagedDocumentLock(documentPath, lock, () -> {
565-
lock.asWriteLock().unlock();
566-
lockTable.released(groupId, path, LockType.DOCUMENT, Lock.LockMode.WRITE_LOCK);
567-
});
539+
return new ManagedSingleLockDocumentLock(documentPath, groupId, lock, Lock.LockMode.WRITE_LOCK, lockTable);
568540
}
569541
}
570542

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to eXist-db by
5+
* Evolved Binary, for the benefit of the eXist-db Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to eXist-db, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in eXist-db.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* ---------------------------------------------------------------------
17+
*
18+
* Copyright (C) 2014, Evolved Binary Ltd
19+
*
20+
* This library is free software; you can redistribute it and/or
21+
* modify it under the terms of the GNU Lesser General Public
22+
* License as published by the Free Software Foundation; version 2.1.
23+
*
24+
* This library is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27+
* Lesser General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU Lesser General Public
30+
* License along with this library; if not, write to the Free Software
31+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32+
*/
33+
package org.exist.storage.lock;
34+
35+
import uk.ac.ic.doc.slurp.multilock.MultiLock;
36+
37+
/**
38+
* @author <a href="mailto:[email protected]">Adam Retter</a>
39+
*/
40+
class LockedPath {
41+
final MultiLock lock;
42+
final Lock.LockMode mode;
43+
final String path;
44+
45+
LockedPath(final MultiLock lock, final Lock.LockMode mode, final String path) {
46+
this.lock = lock;
47+
this.mode = mode;
48+
this.path = path;
49+
}
50+
}

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,40 @@
3333
package org.exist.storage.lock;
3434

3535
import org.exist.xmldb.XmldbURI;
36-
import uk.ac.ic.doc.slurp.multilock.MultiLock;
36+
37+
import javax.annotation.Nullable;
3738

3839
/**
3940
* @author <a href="mailto:[email protected]">Adam Retter</a>
4041
*/
41-
public class ManagedCollectionLock extends ManagedLock<MultiLock[]> {
42+
public class ManagedCollectionLock extends ManagedLock<LockGroup> {
4243

4344
private final XmldbURI collectionUri;
45+
@Nullable private final LockTable lockTable; // NOTE(AR) only null when called via private constructor from {@link #notLocked(XmldbURI)}.
4446

45-
public ManagedCollectionLock(final XmldbURI collectionUri, final MultiLock[] locks, final Runnable closer) {
46-
super(locks, closer);
47+
public ManagedCollectionLock(final XmldbURI collectionUri, final LockGroup lockGroup, final LockTable lockTable) {
48+
super(lockGroup, null); // NOTE(AR) we can set the closer as null here, because we override {@link #close()} below!
4749
this.collectionUri = collectionUri;
50+
this.lockTable = lockTable;
51+
}
52+
53+
private ManagedCollectionLock(final XmldbURI collectionUri) {
54+
this(collectionUri, null, null);
4855
}
4956

5057
public XmldbURI getPath() {
5158
return collectionUri;
5259
}
5360

61+
@Override
62+
public void close() {
63+
if (!closed && lock != null) { // NOTE(AR) only null when constructed from {@link #notLocked(XmldbURI)}.
64+
LockManager.unlockAll(lock.locks, l -> lockTable.released(lock.groupId, l.path, Lock.LockType.COLLECTION, l.mode));
65+
}
66+
this.closed = true;
67+
}
68+
5469
public static ManagedCollectionLock notLocked(final XmldbURI collectionUri) {
55-
return new ManagedCollectionLock(collectionUri, null, () -> {});
70+
return new ManagedCollectionLock(collectionUri);
5671
}
5772
}

0 commit comments

Comments
 (0)