Skip to content

Commit d0eb3ab

Browse files
committed
GH-4784 cherry pick new deadlock approach from RDF4J 5.0.0
1 parent 72a25ce commit d0eb3ab

8 files changed

Lines changed: 690 additions & 215 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
******************************************************************************/
11+
package org.eclipse.rdf4j.common.concurrent.locks;
12+
13+
import java.util.concurrent.atomic.AtomicLong;
14+
import java.util.concurrent.atomic.AtomicReference;
15+
16+
import org.eclipse.rdf4j.common.annotation.InternalUseOnly;
17+
import org.eclipse.rdf4j.common.concurrent.locks.diagnostics.LockCleaner;
18+
import org.eclipse.rdf4j.common.concurrent.locks.diagnostics.LockMonitoring;
19+
import org.eclipse.rdf4j.common.concurrent.locks.diagnostics.LockTracking;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
/**
24+
* A simple reentrant lock that allows other threads to unlock the lock.
25+
*
26+
* @author Håvard M. Ottestad
27+
*/
28+
@InternalUseOnly
29+
public class ExclusiveReentrantLockManager {
30+
31+
private final static Logger logger = LoggerFactory.getLogger(ExclusiveReentrantLockManager.class);
32+
33+
// the underlying lock object
34+
final AtomicLong activeLocks = new AtomicLong();
35+
final AtomicReference<Thread> owner = new AtomicReference<>();
36+
37+
private final int waitToCollect;
38+
39+
LockMonitoring<ExclusiveReentrantLock> lockMonitoring;
40+
41+
public ExclusiveReentrantLockManager() {
42+
this(false);
43+
}
44+
45+
public ExclusiveReentrantLockManager(boolean trackLocks) {
46+
this(trackLocks, LockMonitoring.INITIAL_WAIT_TO_COLLECT);
47+
}
48+
49+
public ExclusiveReentrantLockManager(boolean trackLocks, int collectionFrequency) {
50+
51+
this.waitToCollect = collectionFrequency;
52+
53+
if (trackLocks || Properties.lockTrackingEnabled()) {
54+
55+
lockMonitoring = new LockTracking(
56+
true,
57+
"ExclusiveReentrantLockManager",
58+
LoggerFactory.getLogger(this.getClass()),
59+
waitToCollect,
60+
Lock.ExtendedSupplier.wrap(this::getExclusiveLockInner, this::tryExclusiveLockInner)
61+
);
62+
63+
} else {
64+
lockMonitoring = new LockCleaner(
65+
false,
66+
"ExclusiveReentrantLockManager",
67+
LoggerFactory.getLogger(this.getClass()),
68+
Lock.ExtendedSupplier.wrap(this::getExclusiveLockInner, this::tryExclusiveLockInner)
69+
);
70+
}
71+
72+
}
73+
74+
private Lock tryExclusiveLockInner() {
75+
76+
synchronized (owner) {
77+
if (owner.get() == Thread.currentThread()) {
78+
activeLocks.incrementAndGet();
79+
return new ExclusiveReentrantLock(owner, activeLocks);
80+
}
81+
82+
if (owner.compareAndSet(null, Thread.currentThread())) {
83+
activeLocks.incrementAndGet();
84+
return new ExclusiveReentrantLock(owner, activeLocks);
85+
}
86+
}
87+
88+
return null;
89+
90+
}
91+
92+
private Lock getExclusiveLockInner() throws InterruptedException {
93+
94+
synchronized (owner) {
95+
96+
if (lockMonitoring.requiresManualCleanup()) {
97+
do {
98+
if (Thread.interrupted()) {
99+
throw new InterruptedException();
100+
}
101+
Lock lock = tryExclusiveLockInner();
102+
if (lock != null) {
103+
return lock;
104+
} else {
105+
lockMonitoring.runCleanup();
106+
owner.wait(waitToCollect);
107+
}
108+
} while (true);
109+
} else {
110+
while (true) {
111+
if (Thread.interrupted()) {
112+
throw new InterruptedException();
113+
}
114+
Lock lock = tryExclusiveLockInner();
115+
if (lock != null) {
116+
return lock;
117+
} else {
118+
owner.wait(waitToCollect);
119+
}
120+
}
121+
}
122+
}
123+
}
124+
125+
public Lock tryExclusiveLock() {
126+
return lockMonitoring.tryLock();
127+
}
128+
129+
public Lock getExclusiveLock() throws InterruptedException {
130+
return lockMonitoring.getLock();
131+
}
132+
133+
public boolean isActiveLock() {
134+
return owner.get() != null;
135+
}
136+
137+
static class ExclusiveReentrantLock implements Lock {
138+
139+
final AtomicLong activeLocks;
140+
final AtomicReference<Thread> owner;
141+
private boolean released = false;
142+
143+
public ExclusiveReentrantLock(AtomicReference<Thread> owner, AtomicLong activeLocks) {
144+
this.owner = owner;
145+
this.activeLocks = activeLocks;
146+
}
147+
148+
@Override
149+
public boolean isActive() {
150+
return !released;
151+
}
152+
153+
@Override
154+
public void release() {
155+
if (released) {
156+
throw new IllegalStateException("Lock already released");
157+
}
158+
159+
synchronized (owner) {
160+
if (owner.get() != Thread.currentThread()) {
161+
logger.warn("Releasing lock from different thread, owner: " + owner.get() + ", current: "
162+
+ Thread.currentThread());
163+
}
164+
165+
if (activeLocks.decrementAndGet() == 0) {
166+
owner.set(null);
167+
owner.notifyAll();
168+
}
169+
}
170+
171+
released = true;
172+
173+
}
174+
}
175+
176+
}

0 commit comments

Comments
 (0)