Skip to content
Snippets Groups Projects
Commit 12719133 authored by Damon Kohler's avatar Damon Kohler
Browse files

Fixes locking anti-patterns including double check locking and locking on...

Fixes locking anti-patterns including double check locking and locking on externally accessible objects.
parent 726f086f
Branches
No related tags found
No related merge requests found
Showing
with 84 additions and 57 deletions
......@@ -26,6 +26,9 @@ import java.util.concurrent.ExecutorService;
* @author khughes@google.com (Keith M. Hughes)
*/
public abstract class CancellableLoop implements Runnable {
private final Object mutex;
/**
* {@code true} if the code has been run once, {@code false} otherwise.
*/
......@@ -36,9 +39,13 @@ public abstract class CancellableLoop implements Runnable {
*/
private Thread thread;
public CancellableLoop() {
mutex = new Object();
}
@Override
public void run() {
synchronized (this) {
synchronized (mutex) {
Preconditions.checkState(!ranOnce, "CancellableLoops cannot be restarted.");
ranOnce = true;
thread = Thread.currentThread();
......
......@@ -35,7 +35,8 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Wraps an {@link ScheduledExecutorService} to execute {@link Callable}s with retries.
* Wraps an {@link ScheduledExecutorService} to execute {@link Callable}s with
* retries.
*
* @author damonkohler@google.com (Damon Kohler)
*/
......@@ -47,11 +48,12 @@ public class RetryingExecutorService {
private static final long DEFAULT_RETRY_DELAY = 5;
private static final TimeUnit DEFAULT_RETRY_TIME_UNIT = TimeUnit.SECONDS;
private final ScheduledExecutorService scheduledExecutorService;
private final RetryLoop retryLoop;
private final Map<Callable<Boolean>, CountDownLatch> latches;
private final Map<Future<Boolean>, Callable<Boolean>> callables;
private final CompletionService<Boolean> completionService;
private final RetryLoop retryLoop;
private final ScheduledExecutorService executorService;
private final Object mutex;
private long retryDelay;
private TimeUnit retryTimeUnit;
......@@ -72,7 +74,7 @@ public class RetryingExecutorService {
if (DEBUG) {
log.info("Retry requested.");
}
executorService.schedule(new Runnable() {
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
submit(callable);
......@@ -85,20 +87,21 @@ public class RetryingExecutorService {
}
/**
* @param executorService
* @param scheduledExecutorService
* the {@link ExecutorService} to wrap
*/
public RetryingExecutorService(ScheduledExecutorService executorService) {
this.executorService = executorService;
public RetryingExecutorService(ScheduledExecutorService scheduledExecutorService) {
this.scheduledExecutorService = scheduledExecutorService;
retryLoop = new RetryLoop();
latches = Maps.newConcurrentMap();
callables = Maps.newConcurrentMap();
completionService = new ExecutorCompletionService<Boolean>(executorService);
completionService = new ExecutorCompletionService<Boolean>(scheduledExecutorService);
mutex = new Object();
retryDelay = DEFAULT_RETRY_DELAY;
retryTimeUnit = DEFAULT_RETRY_TIME_UNIT;
running = true;
// TODO(damonkohler): Unify this with the passed in ExecutorService.
executorService.execute(retryLoop);
scheduledExecutorService.execute(retryLoop);
}
/**
......@@ -111,7 +114,8 @@ public class RetryingExecutorService {
* @throws RejectedExecutionException
* if the {@link RetryingExecutorService} is shutting down
*/
public synchronized void submit(Callable<Boolean> callable) {
public void submit(Callable<Boolean> callable) {
synchronized (mutex) {
if (running) {
Future<Boolean> future = completionService.submit(callable);
latches.put(callable, new CountDownLatch(1));
......@@ -120,6 +124,7 @@ public class RetryingExecutorService {
throw new RejectedExecutionException();
}
}
}
/**
* @param delay
......
......@@ -42,6 +42,7 @@ public class ServiceFactory {
private final SlaveServer slaveServer;
private final ServiceManager serviceManager;
private final ScheduledExecutorService executorService;
private final Object mutex;
public ServiceFactory(GraphName nodeName, SlaveServer slaveServer, ServiceManager serviceManager,
ScheduledExecutorService executorService) {
......@@ -49,6 +50,7 @@ public class ServiceFactory {
this.slaveServer = slaveServer;
this.serviceManager = serviceManager;
this.executorService = executorService;
mutex = new Object();
}
/**
......@@ -73,7 +75,7 @@ public class ServiceFactory {
DefaultServiceServer<T, S> serviceServer;
GraphName name = serviceDeclaration.getName();
synchronized (serviceManager) {
synchronized (mutex) {
if (serviceManager.hasServer(name)) {
throw new DuplicateServiceException(String.format("ServiceServer %s already exists.", name));
} else {
......@@ -126,7 +128,7 @@ public class ServiceFactory {
GraphName name = serviceDeclaration.getName();
boolean createdNewClient = false;
synchronized (serviceManager) {
synchronized (mutex) {
if (serviceManager.hasClient(name)) {
serviceClient = (DefaultServiceClient<T, S>) serviceManager.getClient(name);
} else {
......
......@@ -62,6 +62,7 @@ public class DefaultSubscriber<T> extends DefaultTopicParticipant implements Sub
private final IncomingMessageQueue<T> incomingMessageQueue;
private final Set<PublisherIdentifier> knownPublishers;
private final TcpClientManager tcpClientManager;
private final Object mutex;
/**
* Manages the {@link SubscriberListener}s for this {@link Subscriber}.
......@@ -82,6 +83,7 @@ public class DefaultSubscriber<T> extends DefaultTopicParticipant implements Sub
incomingMessageQueue = new IncomingMessageQueue<T>(deserializer, executorService);
knownPublishers = Sets.newHashSet();
tcpClientManager = new TcpClientManager(executorService);
mutex = new Object();
SubscriberHandshakeHandler<T> subscriberHandshakeHandler =
new SubscriberHandshakeHandler<T>(toDeclaration().toConnectionHeader(),
incomingMessageQueue, executorService);
......@@ -138,8 +140,8 @@ public class DefaultSubscriber<T> extends DefaultTopicParticipant implements Sub
}
@VisibleForTesting
public synchronized void addPublisher(PublisherIdentifier publisherIdentifier,
InetSocketAddress address) {
public void addPublisher(PublisherIdentifier publisherIdentifier, InetSocketAddress address) {
synchronized (mutex) {
// TODO(damonkohler): If the connection is dropped, knownPublishers should
// be updated.
if (knownPublishers.contains(publisherIdentifier)) {
......@@ -151,6 +153,7 @@ public class DefaultSubscriber<T> extends DefaultTopicParticipant implements Sub
knownPublishers.add(publisherIdentifier);
signalOnNewPublisher(publisherIdentifier);
}
}
/**
* Updates the list of {@link Publisher}s for the topic that this
......
......@@ -36,6 +36,7 @@ public class PublisherFactory {
private final MessageFactory messageFactory;
private final ScheduledExecutorService executorService;
private final NodeIdentifier nodeIdentifier;
private final Object mutex;
public PublisherFactory(NodeIdentifier nodeIdentifier,
TopicParticipantManager topicParticipantManager, MessageFactory messageFactory,
......@@ -44,6 +45,7 @@ public class PublisherFactory {
this.topicParticipantManager = topicParticipantManager;
this.messageFactory = messageFactory;
this.executorService = executorService;
mutex = new Object();
}
/**
......@@ -63,8 +65,7 @@ public class PublisherFactory {
public <T> Publisher<T> newOrExisting(TopicDeclaration topicDeclaration,
MessageSerializer<T> messageSerializer) {
GraphName topicName = topicDeclaration.getName();
synchronized (topicParticipantManager) {
synchronized (mutex) {
if (topicParticipantManager.hasPublisher(topicName)) {
return (DefaultPublisher<T>) topicParticipantManager.getPublisher(topicName);
} else {
......
......@@ -34,12 +34,14 @@ public class SubscriberFactory {
private final NodeIdentifier nodeIdentifier;
private final TopicParticipantManager topicParticipantManager;
private final ScheduledExecutorService executorService;
private final Object mutex;
public SubscriberFactory(NodeIdentifier nodeIdentifier,
TopicParticipantManager topicParticipantManager, ScheduledExecutorService executorService) {
this.nodeIdentifier = nodeIdentifier;
this.topicParticipantManager = topicParticipantManager;
this.executorService = executorService;
mutex = new Object();
}
/**
......@@ -60,7 +62,7 @@ public class SubscriberFactory {
MessageDeserializer<T> messageDeserializer) {
GraphName topicName = topicDeclaration.getName();
synchronized (topicParticipantManager) {
synchronized (mutex) {
if (topicParticipantManager.hasSubscriber(topicName)) {
return (DefaultSubscriber<T>) topicParticipantManager.getSubscriber(topicName);
} else {
......
......@@ -54,9 +54,7 @@ public class LazyMessage<T> {
@VisibleForTesting
LazyMessage(T message) {
buffer = null;
deserializer = null;
mutex = null;
this(null, null);
this.message = message;
}
......@@ -64,14 +62,12 @@ public class LazyMessage<T> {
* @return the deserialized message
*/
public T get() {
synchronized (mutex) {
if (message != null) {
return message;
}
synchronized (mutex) {
if (message == null) {
message = deserializer.deserialize(buffer);
}
}
return message;
}
}
\ No newline at end of file
......@@ -50,6 +50,7 @@ public class OutgoingMessageQueue<T> {
private final Writer writer;
private final MessageBufferPool messageBufferPool;
private final ChannelBuffer latchedBuffer;
private final Object mutex;
private boolean latchMode;
private T latchedMessage;
......@@ -84,6 +85,7 @@ public class OutgoingMessageQueue<T> {
writer = new Writer();
messageBufferPool = new MessageBufferPool();
latchedBuffer = MessageBuffers.dynamicBuffer();
mutex = new Object();
latchMode = false;
executorService.execute(writer);
}
......@@ -105,9 +107,11 @@ public class OutgoingMessageQueue<T> {
setLatchedMessage(message);
}
private synchronized void setLatchedMessage(T message) {
private void setLatchedMessage(T message) {
synchronized (mutex) {
latchedMessage = message;
}
}
/**
* Stop writing messages and close all outgoing connections.
......@@ -134,11 +138,13 @@ public class OutgoingMessageQueue<T> {
// TODO(damonkohler): Avoid re-serializing the latched message if it hasn't
// changed.
private synchronized void writeLatchedMessage(Channel channel) {
private void writeLatchedMessage(Channel channel) {
synchronized (mutex) {
latchedBuffer.clear();
serializer.serialize(latchedMessage, latchedBuffer);
channel.write(latchedBuffer);
}
}
/**
* @return the number of {@link Channel}s which have been added to this queue
......
......@@ -35,6 +35,8 @@ import java.util.concurrent.TimeUnit;
*/
public class SwitchableMasterUriProvider implements MasterUriProvider {
private final Object mutex;
/**
* The current provider in use.
*/
......@@ -51,6 +53,7 @@ public class SwitchableMasterUriProvider implements MasterUriProvider {
*/
public SwitchableMasterUriProvider(MasterUriProvider provider) {
this.provider = provider;
mutex = new Object();
}
@Override
......@@ -58,7 +61,7 @@ public class SwitchableMasterUriProvider implements MasterUriProvider {
MasterUriProvider providerToUse = null;
ProviderRequest requestToUse = null;
synchronized (this) {
synchronized (mutex) {
if (provider != null) {
providerToUse = provider;
} else {
......@@ -80,7 +83,7 @@ public class SwitchableMasterUriProvider implements MasterUriProvider {
// seems appropriate to wait rather than to return immediately.
MasterUriProvider providerToUse = null;
synchronized (this) {
synchronized (mutex) {
if (provider != null) {
providerToUse = provider;
}
......@@ -105,7 +108,8 @@ public class SwitchableMasterUriProvider implements MasterUriProvider {
* @param switcher
* the new provider
*/
public synchronized void switchProvider(MasterUriProviderSwitcher switcher) {
public void switchProvider(MasterUriProviderSwitcher switcher) {
synchronized (mutex) {
MasterUriProvider oldProvider = provider;
provider = switcher.switchProvider(oldProvider);
......@@ -116,6 +120,7 @@ public class SwitchableMasterUriProvider implements MasterUriProvider {
pending.clear();
}
}
}
/**
* Perform a switch between {@link MasterUriProvider} instances for the
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment