提交 87d8d72c 编写于 作者: M Matteo Merli 提交者: GitHub

In MockZooKeeper triggers callback without holding mutex (#460)

上级 11f874e2
...@@ -22,6 +22,7 @@ import java.util.TreeMap; ...@@ -22,6 +22,7 @@ import java.util.TreeMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.bookkeeper.mledger.util.Pair; import org.apache.bookkeeper.mledger.util.Pair;
import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.Children2Callback;
...@@ -51,7 +52,7 @@ import sun.reflect.ReflectionFactory; ...@@ -51,7 +52,7 @@ import sun.reflect.ReflectionFactory;
public class MockZooKeeper extends ZooKeeper { public class MockZooKeeper extends ZooKeeper {
private TreeMap<String, Pair<String, Integer>> tree; private TreeMap<String, Pair<String, Integer>> tree;
private SetMultimap<String, Watcher> watchers; private SetMultimap<String, Watcher> watchers;
private boolean stopped; private volatile boolean stopped;
private boolean alwaysFail = false; private boolean alwaysFail = false;
private ExecutorService executor; private ExecutorService executor;
...@@ -62,6 +63,8 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -62,6 +63,8 @@ public class MockZooKeeper extends ZooKeeper {
private long sessionId = 0L; private long sessionId = 0L;
private int readOpDelayMs; private int readOpDelayMs;
private ReentrantLock mutex;
public static MockZooKeeper newInstance() { public static MockZooKeeper newInstance() {
return newInstance(null); return newInstance(null);
} }
...@@ -78,6 +81,7 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -78,6 +81,7 @@ public class MockZooKeeper extends ZooKeeper {
MockZooKeeper zk = MockZooKeeper.class.cast(intConstr.newInstance()); MockZooKeeper zk = MockZooKeeper.class.cast(intConstr.newInstance());
zk.init(executor); zk.init(executor);
zk.readOpDelayMs = readOpDelayMs; zk.readOpDelayMs = readOpDelayMs;
zk.mutex = new ReentrantLock();
return zk; return zk;
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw e; throw e;
...@@ -116,13 +120,17 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -116,13 +120,17 @@ public class MockZooKeeper extends ZooKeeper {
} }
@Override @Override
public synchronized void register(Watcher watcher) { public void register(Watcher watcher) {
mutex.lock();
sessionWatcher = watcher; sessionWatcher = watcher;
mutex.unlock();
} }
@Override @Override
public synchronized String create(String path, byte[] data, List<ACL> acl, CreateMode createMode) public String create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
throws KeeperException, InterruptedException { throws KeeperException, InterruptedException {
mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
if (stopped) if (stopped)
...@@ -153,17 +161,20 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -153,17 +161,20 @@ public class MockZooKeeper extends ZooKeeper {
toNotifyParent.addAll(watchers.get(parent)); toNotifyParent.addAll(watchers.get(parent));
executor.execute(() -> { executor.execute(() -> {
toNotifyParent.forEach(watcher -> watcher toNotifyParent.forEach(watcher -> watcher.process(
.process(new WatchedEvent(EventType.NodeChildrenChanged, KeeperState.SyncConnected, parent))); new WatchedEvent(EventType.NodeChildrenChanged, KeeperState.SyncConnected, parent)));
}); });
} }
return path; return path;
} finally {
mutex.unlock();
}
} }
@Override @Override
public synchronized void create(final String path, final byte[] data, final List<ACL> acl, CreateMode createMode, public void create(final String path, final byte[] data, final List<ACL> acl, CreateMode createMode,
final StringCallback cb, final Object ctx) { final StringCallback cb, final Object ctx) {
if (stopped) { if (stopped) {
cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null); cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null);
...@@ -173,29 +184,36 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -173,29 +184,36 @@ public class MockZooKeeper extends ZooKeeper {
executor.execute(() -> { executor.execute(() -> {
String parent = path.substring(0, path.lastIndexOf("/")); String parent = path.substring(0, path.lastIndexOf("/"));
synchronized (MockZooKeeper.this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null); cb.processResult(failReturnCode.intValue(), path, ctx, null);
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null); cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null);
} else if (tree.containsKey(path)) { } else if (tree.containsKey(path)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NODEEXISTS.intValue(), path, ctx, null); cb.processResult(KeeperException.Code.NODEEXISTS.intValue(), path, ctx, null);
} else if (!parent.isEmpty() && !tree.containsKey(parent)) { } else if (!parent.isEmpty() && !tree.containsKey(parent)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx, null); cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx, null);
} else { } else {
tree.put(path, Pair.create(new String(data), 0)); tree.put(path, Pair.create(new String(data), 0));
mutex.unlock();
cb.processResult(0, path, ctx, null); cb.processResult(0, path, ctx, null);
if (!parent.isEmpty()) { if (!parent.isEmpty()) {
watchers.get(parent).forEach(watcher -> watcher.process( watchers.get(parent).forEach(watcher -> watcher.process(
new WatchedEvent(EventType.NodeChildrenChanged, KeeperState.SyncConnected, parent))); new WatchedEvent(EventType.NodeChildrenChanged, KeeperState.SyncConnected, parent)));
} }
} }
}
}); });
} }
@Override @Override
public synchronized byte[] getData(String path, Watcher watcher, Stat stat) throws KeeperException { public byte[] getData(String path, Watcher watcher, Stat stat) throws KeeperException {
mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
Pair<String, Integer> value = tree.get(path); Pair<String, Integer> value = tree.get(path);
if (value == null) { if (value == null) {
...@@ -209,6 +227,9 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -209,6 +227,9 @@ public class MockZooKeeper extends ZooKeeper {
} }
return value.first.getBytes(); return value.first.getBytes();
} }
} finally {
mutex.unlock();
}
} }
@Override @Override
...@@ -224,8 +245,11 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -224,8 +245,11 @@ public class MockZooKeeper extends ZooKeeper {
} }
Pair<String, Integer> value; Pair<String, Integer> value;
synchronized (MockZooKeeper.this) { mutex.lock();
try {
value = tree.get(path); value = tree.get(path);
} finally {
mutex.unlock();
} }
if (value == null) { if (value == null) {
...@@ -242,17 +266,20 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -242,17 +266,20 @@ public class MockZooKeeper extends ZooKeeper {
public void getData(final String path, final Watcher watcher, final DataCallback cb, final Object ctx) { public void getData(final String path, final Watcher watcher, final DataCallback cb, final Object ctx) {
executor.execute(() -> { executor.execute(() -> {
checkReadOpDelay(); checkReadOpDelay();
synchronized (MockZooKeeper.this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null, null); cb.processResult(failReturnCode.intValue(), path, ctx, null, null);
return; return;
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null, null); cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx, null, null);
return; return;
} }
Pair<String, Integer> value = tree.get(path); Pair<String, Integer> value = tree.get(path);
if (value == null) { if (value == null) {
mutex.unlock();
cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx, null, null); cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx, null, null);
} else { } else {
if (watcher != null) { if (watcher != null) {
...@@ -261,20 +288,22 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -261,20 +288,22 @@ public class MockZooKeeper extends ZooKeeper {
Stat stat = new Stat(); Stat stat = new Stat();
stat.setVersion(value.second); stat.setVersion(value.second);
mutex.unlock();
cb.processResult(0, path, ctx, value.first.getBytes(), stat); cb.processResult(0, path, ctx, value.first.getBytes(), stat);
} }
}
}); });
} }
@Override @Override
public void getChildren(final String path, final Watcher watcher, final ChildrenCallback cb, final Object ctx) { public void getChildren(final String path, final Watcher watcher, final ChildrenCallback cb, final Object ctx) {
executor.execute(() -> { executor.execute(() -> {
synchronized (MockZooKeeper.this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null); cb.processResult(failReturnCode.intValue(), path, ctx, null);
return; return;
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null); cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null);
return; return;
} }
...@@ -295,16 +324,19 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -295,16 +324,19 @@ public class MockZooKeeper extends ZooKeeper {
} }
} }
mutex.unlock();
cb.processResult(0, path, ctx, children); cb.processResult(0, path, ctx, children);
if (watcher != null) { if (watcher != null) {
watchers.put(path, watcher); watchers.put(path, watcher);
} }
}
}); });
} }
@Override @Override
public synchronized List<String> getChildren(String path, Watcher watcher) throws KeeperException { public List<String> getChildren(String path, Watcher watcher) throws KeeperException {
mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
if (!tree.containsKey(path)) { if (!tree.containsKey(path)) {
...@@ -332,11 +364,15 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -332,11 +364,15 @@ public class MockZooKeeper extends ZooKeeper {
} }
return children; return children;
} finally {
mutex.unlock();
}
} }
@Override @Override
public synchronized List<String> getChildren(String path, boolean watch) public List<String> getChildren(String path, boolean watch) throws KeeperException, InterruptedException {
throws KeeperException, InterruptedException { mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
if (stopped) { if (stopped) {
...@@ -361,19 +397,25 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -361,19 +397,25 @@ public class MockZooKeeper extends ZooKeeper {
} }
} }
return children; return children;
} finally {
mutex.unlock();
}
} }
@Override @Override
public void getChildren(final String path, boolean watcher, final Children2Callback cb, final Object ctx) { public void getChildren(final String path, boolean watcher, final Children2Callback cb, final Object ctx) {
executor.execute(() -> { executor.execute(() -> {
synchronized (MockZooKeeper.this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null, null); cb.processResult(failReturnCode.intValue(), path, ctx, null, null);
return; return;
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null, null); cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null, null);
return; return;
} else if (!tree.containsKey(path)) { } else if (!tree.containsKey(path)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NoNode, path, ctx, null, null); cb.processResult(KeeperException.Code.NoNode, path, ctx, null, null);
return; return;
} }
...@@ -396,13 +438,16 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -396,13 +438,16 @@ public class MockZooKeeper extends ZooKeeper {
} }
log.debug("getChildren done path={} result={}", path, children); log.debug("getChildren done path={} result={}", path, children);
mutex.unlock();
cb.processResult(0, path, ctx, children, new Stat()); cb.processResult(0, path, ctx, children, new Stat());
}
}); });
} }
@Override @Override
public synchronized Stat exists(String path, boolean watch) throws KeeperException, InterruptedException { public Stat exists(String path, boolean watch) throws KeeperException, InterruptedException {
mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
if (stopped) if (stopped)
...@@ -415,10 +460,15 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -415,10 +460,15 @@ public class MockZooKeeper extends ZooKeeper {
} else { } else {
return null; return null;
} }
} finally {
mutex.unlock();
}
} }
@Override @Override
public synchronized Stat exists(String path, Watcher watcher) throws KeeperException, InterruptedException { public Stat exists(String path, Watcher watcher) throws KeeperException, InterruptedException {
mutex.lock();
try {
checkProgrammedFail(); checkProgrammedFail();
if (stopped) if (stopped)
...@@ -435,32 +485,37 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -435,32 +485,37 @@ public class MockZooKeeper extends ZooKeeper {
} else { } else {
return null; return null;
} }
} finally {
mutex.unlock();
}
} }
public void exists(String path, boolean watch, StatCallback cb, Object ctx) { public void exists(String path, boolean watch, StatCallback cb, Object ctx) {
executor.execute(() -> { executor.execute(() -> {
synchronized (this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null); cb.processResult(failReturnCode.intValue(), path, ctx, null);
return; return;
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null); cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null);
return; return;
} }
if (tree.containsKey(path)) { if (tree.containsKey(path)) {
mutex.unlock();
cb.processResult(0, path, ctx, new Stat()); cb.processResult(0, path, ctx, new Stat());
} else { } else {
mutex.unlock();
cb.processResult(KeeperException.Code.NoNode, path, ctx, null); cb.processResult(KeeperException.Code.NoNode, path, ctx, null);
} }
}
}); });
} }
@Override @Override
public void sync(String path, VoidCallback cb, Object ctx) { public void sync(String path, VoidCallback cb, Object ctx) {
executor.execute(() -> { executor.execute(() -> {
synchronized (this) {
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
cb.processResult(failReturnCode.intValue(), path, ctx); cb.processResult(failReturnCode.intValue(), path, ctx);
return; return;
...@@ -470,18 +525,18 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -470,18 +525,18 @@ public class MockZooKeeper extends ZooKeeper {
} }
cb.processResult(0, path, ctx); cb.processResult(0, path, ctx);
}
}); });
} }
@Override @Override
public Stat setData(final String path, byte[] data, int version) throws KeeperException, InterruptedException { public Stat setData(final String path, byte[] data, int version) throws KeeperException, InterruptedException {
mutex.lock();
final Set<Watcher> toNotify = Sets.newHashSet(); final Set<Watcher> toNotify = Sets.newHashSet();
int newVersion; int newVersion;
synchronized (this) { try {
checkProgrammedFail(); checkProgrammedFail();
if (stopped) { if (stopped) {
...@@ -505,6 +560,8 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -505,6 +560,8 @@ public class MockZooKeeper extends ZooKeeper {
toNotify.addAll(watchers.get(path)); toNotify.addAll(watchers.get(path));
watchers.removeAll(path); watchers.removeAll(path);
} finally {
mutex.unlock();
} }
executor.execute(() -> { executor.execute(() -> {
...@@ -518,8 +575,7 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -518,8 +575,7 @@ public class MockZooKeeper extends ZooKeeper {
} }
@Override @Override
public synchronized void setData(final String path, final byte[] data, int version, final StatCallback cb, public void setData(final String path, final byte[] data, int version, final StatCallback cb, final Object ctx) {
final Object ctx) {
if (stopped) { if (stopped) {
cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null); cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null);
return; return;
...@@ -528,16 +584,20 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -528,16 +584,20 @@ public class MockZooKeeper extends ZooKeeper {
executor.execute(() -> { executor.execute(() -> {
final Set<Watcher> toNotify = Sets.newHashSet(); final Set<Watcher> toNotify = Sets.newHashSet();
synchronized (MockZooKeeper.this) { mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx, null); cb.processResult(failReturnCode.intValue(), path, ctx, null);
return; return;
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null); cb.processResult(KeeperException.Code.ConnectionLoss, path, ctx, null);
return; return;
} }
if (!tree.containsKey(path)) { if (!tree.containsKey(path)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NoNode, path, ctx, null); cb.processResult(KeeperException.Code.NoNode, path, ctx, null);
return; return;
} }
...@@ -547,6 +607,7 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -547,6 +607,7 @@ public class MockZooKeeper extends ZooKeeper {
// Check version // Check version
if (version != -1 && version != currentVersion) { if (version != -1 && version != currentVersion) {
log.debug("[{}] Current version: {} -- Expected: {}", path, currentVersion, version); log.debug("[{}] Current version: {} -- Expected: {}", path, currentVersion, version);
mutex.unlock();
cb.processResult(KeeperException.Code.BadVersion, path, ctx, null); cb.processResult(KeeperException.Code.BadVersion, path, ctx, null);
return; return;
} }
...@@ -556,12 +617,12 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -556,12 +617,12 @@ public class MockZooKeeper extends ZooKeeper {
tree.put(path, Pair.create(new String(data), newVersion)); tree.put(path, Pair.create(new String(data), newVersion));
Stat stat = new Stat(); Stat stat = new Stat();
stat.setVersion(newVersion); stat.setVersion(newVersion);
cb.processResult(0, path, ctx, stat);
mutex.unlock();
cb.processResult(0, path, ctx, stat);
toNotify.addAll(watchers.get(path)); toNotify.addAll(watchers.get(path));
watchers.removeAll(path); watchers.removeAll(path);
}
for (Watcher watcher : toNotify) { for (Watcher watcher : toNotify) {
watcher.process(new WatchedEvent(EventType.NodeDataChanged, KeeperState.SyncConnected, path)); watcher.process(new WatchedEvent(EventType.NodeDataChanged, KeeperState.SyncConnected, path));
...@@ -577,7 +638,8 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -577,7 +638,8 @@ public class MockZooKeeper extends ZooKeeper {
final Set<Watcher> toNotifyParent; final Set<Watcher> toNotifyParent;
final String parent; final String parent;
synchronized (this) { mutex.lock();
try {
if (stopped) { if (stopped) {
throw new KeeperException.ConnectionLossException(); throw new KeeperException.ConnectionLossException();
} else if (!tree.containsKey(path)) { } else if (!tree.containsKey(path)) {
...@@ -605,6 +667,8 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -605,6 +667,8 @@ public class MockZooKeeper extends ZooKeeper {
} }
watchers.removeAll(path); watchers.removeAll(path);
} finally {
mutex.unlock();
} }
executor.execute(() -> { executor.execute(() -> {
...@@ -622,8 +686,10 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -622,8 +686,10 @@ public class MockZooKeeper extends ZooKeeper {
} }
@Override @Override
public synchronized void delete(final String path, int version, final VoidCallback cb, final Object ctx) { public void delete(final String path, int version, final VoidCallback cb, final Object ctx) {
mutex.lock();
if (executor.isShutdown()) { if (executor.isShutdown()) {
mutex.unlock();
cb.processResult(KeeperException.Code.SESSIONEXPIRED.intValue(), path, ctx); cb.processResult(KeeperException.Code.SESSIONEXPIRED.intValue(), path, ctx);
return; return;
} }
...@@ -638,13 +704,19 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -638,13 +704,19 @@ public class MockZooKeeper extends ZooKeeper {
} }
executor.execute(() -> { executor.execute(() -> {
mutex.lock();
if (getProgrammedFailStatus()) { if (getProgrammedFailStatus()) {
mutex.unlock();
cb.processResult(failReturnCode.intValue(), path, ctx); cb.processResult(failReturnCode.intValue(), path, ctx);
} else if (stopped) { } else if (stopped) {
mutex.unlock();
cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx); cb.processResult(KeeperException.Code.CONNECTIONLOSS.intValue(), path, ctx);
} else if (!tree.containsKey(path)) { } else if (!tree.containsKey(path)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx); cb.processResult(KeeperException.Code.NONODE.intValue(), path, ctx);
} else if (hasChildren(path)) { } else if (hasChildren(path)) {
mutex.unlock();
cb.processResult(KeeperException.Code.NOTEMPTY.intValue(), path, ctx); cb.processResult(KeeperException.Code.NOTEMPTY.intValue(), path, ctx);
} else { } else {
if (version != -1) { if (version != -1) {
...@@ -656,6 +728,8 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -656,6 +728,8 @@ public class MockZooKeeper extends ZooKeeper {
} }
tree.remove(path); tree.remove(path);
mutex.unlock();
cb.processResult(0, path, ctx); cb.processResult(0, path, ctx);
toNotifyDelete.forEach(watcher -> watcher toNotifyDelete.forEach(watcher -> watcher
...@@ -666,17 +740,23 @@ public class MockZooKeeper extends ZooKeeper { ...@@ -666,17 +740,23 @@ public class MockZooKeeper extends ZooKeeper {
}); });
watchers.removeAll(path); watchers.removeAll(path);
mutex.unlock();
} }
@Override @Override
public void close() throws InterruptedException { public void close() throws InterruptedException {
} }
public synchronized void shutdown() throws InterruptedException { public void shutdown() throws InterruptedException {
mutex.lock();
try {
stopped = true; stopped = true;
tree.clear(); tree.clear();
watchers.clear(); watchers.clear();
executor.shutdownNow(); executor.shutdownNow();
} finally {
mutex.unlock();
}
} }
void checkProgrammedFail() throws KeeperException { void checkProgrammedFail() throws KeeperException {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册