com.maxifier.mxcache.DependencyTrackingAction Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2014 Maxifier Ltd. All Rights Reserved.
*/
package com.maxifier.mxcache;
import com.maxifier.mxcache.caches.CleaningNode;
import com.maxifier.mxcache.impl.resource.AbstractDependencyNode;
import com.maxifier.mxcache.impl.resource.DependencyNode;
import com.maxifier.mxcache.impl.resource.DependencyTracker;
import com.maxifier.mxcache.impl.resource.ResourceOccupied;
import com.maxifier.mxcache.util.TIdentityHashSet;
import javax.annotation.Nonnull;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Lock;
/**
* DependencyTrackingAction - use this action if you need to maintain you own cache.
*
* To do this, you need to:
*
* - Create an instance of DependencyTrackingAction. Take care of this instance: it should not be GC'ed!
* - Wrap all your code that creates dependencies to Callable or Runnable and pass it to trackDependencies() of instance
*
*
* This class may be also used to prevent deadlocks. If the code you invoke with trackDependencies makes use of any
* resource which is being written at the moment MxCache will unroll the stack by throwing ResourceOccupied error
* and then it will apply your change again.
*
* @author Alexander Kochurov ([email protected]) (2012-10-26 15:44)
*/
public class DependencyTrackingAction {
private final DependencyNode dependencyNode = new DependencyNodeImpl();
public T trackDependencies(Callable callable) throws Exception {
while (true) {
DependencyNode oldNode = null;
try {
oldNode = DependencyTracker.track(dependencyNode);
try {
return callable.call();
} finally {
DependencyTracker.exit(oldNode);
}
} catch (ResourceOccupied e) {
if (oldNode != null) {
throw e;
}
e.getResource().waitForEndOfModification();
}
}
}
public T trackDependencies(CallableWithoutExceptions callable) {
while (true) {
DependencyNode oldNode = null;
try {
oldNode = DependencyTracker.track(dependencyNode);
try {
return callable.call();
} finally {
DependencyTracker.exit(oldNode);
}
} catch (ResourceOccupied e) {
if (oldNode != null) {
throw e;
}
e.getResource().waitForEndOfModification();
}
}
}
public void trackDependencies(Runnable callable) {
while (true) {
DependencyNode oldNode = null;
try {
oldNode = DependencyTracker.track(dependencyNode);
try {
callable.run();
return;
} finally {
DependencyTracker.exit(oldNode);
}
} catch (ResourceOccupied e) {
if (oldNode != null) {
throw e;
}
e.getResource().waitForEndOfModification();
}
}
}
public void mark() {
DependencyTracker.mark(dependencyNode);
}
/** Override this method if you want to clear any caches */
protected void onClear() {
// do nothing
}
private class DependencyNodeImpl extends AbstractDependencyNode implements CleaningNode {
private final Lock lock = new LightweightLock();
@Override
public void appendNodes(TIdentityHashSet elements) {
elements.add(this);
}
@Override
public void addNode(@Nonnull CleaningNode cache) {
throw new UnsupportedOperationException();
}
@Override
public Lock getLock() {
return lock;
}
@Override
public void clear() {
onClear();
}
@Override
public DependencyNode getDependencyNode() {
return this;
}
@Override
public Object getCacheOwner() {
return null;
}
}
}