fix(RendererWorkingSetImpl): break lock-order deadlock between commit and get
This commit is contained in:
@@ -18,6 +18,8 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
|
||||
private final ConcurrentMap<String, RendererWork> workingSet = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentMap<String, Object> commitLocks = new ConcurrentHashMap<>();
|
||||
|
||||
private final BlockingQueue<Map<String, RenderingBo>> mapsPool = new ArrayBlockingQueue<>(100);
|
||||
|
||||
private final RenderingBoPool rendererBoPool;
|
||||
@@ -82,7 +84,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
public RenderingBo get(final String producerId, final String id) {
|
||||
final var next = this.getWorkingSetByProducer(producerId).next();
|
||||
synchronized (next) {
|
||||
final var bo = this.getWorkingSetByProducer(producerId).next().get(id);
|
||||
final var bo = next.get(id);
|
||||
if (bo == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
public <T extends RenderingBo> T get(final String producerId, final String id, final Class<T> type) {
|
||||
final var next = this.getWorkingSetByProducer(producerId).next();
|
||||
synchronized (next) {
|
||||
final RenderingBo bo = this.getWorkingSetByProducer(producerId).next().get(id);
|
||||
final RenderingBo bo = next.get(id);
|
||||
if (bo == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -123,29 +125,28 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
|
||||
@Override
|
||||
public Map<String, RenderingBo> commit(final String producerId, final boolean clone) {
|
||||
final AtomicReference<Map<String, RenderingBo>> previousLiveRef = new AtomicReference<>();
|
||||
synchronized (this.commitLockFor(producerId)) {
|
||||
final AtomicReference<RendererWork> previousWorkRef = new AtomicReference<>();
|
||||
final RendererWork rendererWork = this.workingSet.compute(producerId, (k, v) -> {
|
||||
if (v == null) {
|
||||
return new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool());
|
||||
}
|
||||
previousLiveRef.set(v.live());
|
||||
if (clone) {
|
||||
final Map<String, RenderingBo> newNext = this.obtainMapFromPool();
|
||||
synchronized (v.next()) {
|
||||
v.next().entrySet().forEach(e -> {
|
||||
final RenderingBo copy = this.rendererBoPool.copy(e.getValue());
|
||||
newNext.put(e.getKey(), copy);
|
||||
});
|
||||
}
|
||||
return new RendererWork(v.next(), newNext);
|
||||
}
|
||||
else {
|
||||
previousWorkRef.set(v);
|
||||
return new RendererWork(v.next(), this.obtainMapFromPool());
|
||||
}
|
||||
});
|
||||
|
||||
final var previousLiveSet = previousLiveRef.get();
|
||||
if (previousLiveSet != null) {
|
||||
final var previousWork = previousWorkRef.get();
|
||||
if (previousWork != null) {
|
||||
if (clone) {
|
||||
final var oldNext = previousWork.next();
|
||||
synchronized (oldNext) {
|
||||
oldNext.entrySet().forEach(e -> {
|
||||
final RenderingBo copy = this.rendererBoPool.copy(e.getValue());
|
||||
rendererWork.next().put(e.getKey(), copy);
|
||||
});
|
||||
}
|
||||
}
|
||||
final var previousLiveSet = previousWork.live();
|
||||
if (clone) {
|
||||
synchronized (previousLiveSet) {
|
||||
previousLiveSet.values().forEach(this.rendererBoPool::returnToPool);
|
||||
@@ -156,6 +157,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
|
||||
return rendererWork.next();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearNext(final String producerId) {
|
||||
@@ -168,6 +170,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
|
||||
@Override
|
||||
public void clear(final String producerId) {
|
||||
synchronized (this.commitLockFor(producerId)) {
|
||||
final RendererWork rendererWork = this.workingSet.remove(producerId);
|
||||
if (rendererWork != null) {
|
||||
synchronized (rendererWork.live()) {
|
||||
@@ -178,6 +181,8 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.commitLocks.remove(producerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLiveSet(final List<RenderingBo> combinedLiveSet, final Predicate<String> filter) {
|
||||
@@ -194,6 +199,10 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
return this.workingSet.computeIfAbsent(producerId, k -> new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool()));
|
||||
}
|
||||
|
||||
private Object commitLockFor(final String producerId) {
|
||||
return this.commitLocks.computeIfAbsent(producerId, k -> new Object());
|
||||
}
|
||||
|
||||
private Map<String, RenderingBo> obtainMapFromPool() {
|
||||
var map = this.mapsPool.poll();
|
||||
if (map == null) {
|
||||
|
||||
Reference in New Issue
Block a user