diff --git a/src/main/java/com/rabbitmq/client/impl/AMQChannel.java b/src/main/java/com/rabbitmq/client/impl/AMQChannel.java index 0695c8072f..8965704811 100644 --- a/src/main/java/com/rabbitmq/client/impl/AMQChannel.java +++ b/src/main/java/com/rabbitmq/client/impl/AMQChannel.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; /** @@ -54,7 +56,8 @@ public abstract class AMQChannel extends ShutdownNotifierComponent { * so that clients can themselves use the channel to synchronize * on. */ - protected final Object _channelMutex = new Object(); + protected final ReentrantLock _channelLock = new ReentrantLock(); + protected final Condition _channelLockCondition = _channelLock.newCondition(); /** The connection this channel is associated with. */ private final AMQConnection _connection; @@ -191,7 +194,8 @@ public void handleCompleteInboundCommand(AMQCommand command) throws IOException // so it must be a response to an earlier RPC. if (_checkRpcResponseType) { - synchronized (_channelMutex) { + _channelLock.lock(); + try { // check if this reply command is intended for the current waiting request before calling nextOutstandingRpc() if (_activeRpc != null && !_activeRpc.canHandleReply(command)) { // this reply command is not intended for the current waiting request @@ -199,6 +203,8 @@ public void handleCompleteInboundCommand(AMQCommand command) throws IOException // Throw this reply command away so we don't stop the current request from waiting for its reply return; } + } finally { + _channelLock.unlock(); } } final RpcWrapper nextOutstandingRpc = nextOutstandingRpc(); @@ -220,11 +226,12 @@ public void enqueueAsyncRpc(Method method, CompletableFuture future) { } private void doEnqueueRpc(Supplier rpcWrapperSupplier) { - synchronized (_channelMutex) { + _channelLock.lock(); + try { boolean waitClearedInterruptStatus = false; while (_activeRpc != null) { try { - _channelMutex.wait(); + _channelLockCondition.await(); } catch (InterruptedException e) { //NOSONAR waitClearedInterruptStatus = true; // No Sonar: we re-interrupt the thread later @@ -234,23 +241,31 @@ private void doEnqueueRpc(Supplier rpcWrapperSupplier) { Thread.currentThread().interrupt(); } _activeRpc = rpcWrapperSupplier.get(); + } finally { + _channelLock.unlock(); } } public boolean isOutstandingRpc() { - synchronized (_channelMutex) { + _channelLock.lock(); + try { return (_activeRpc != null); + } finally { + _channelLock.unlock(); } } public RpcWrapper nextOutstandingRpc() { - synchronized (_channelMutex) { + _channelLock.lock(); + try { RpcWrapper result = _activeRpc; _activeRpc = null; - _channelMutex.notifyAll(); + _channelLockCondition.signalAll(); return result; + } finally { + _channelLock.unlock(); } } @@ -344,36 +359,48 @@ private AMQCommand privateRpc(Method m, int timeout) public void rpc(Method m, RpcContinuation k) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { ensureIsOpen(); quiescingRpc(m, k); + } finally { + _channelLock.unlock(); } } public void quiescingRpc(Method m, RpcContinuation k) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { enqueueRpc(k); quiescingTransmit(m); + } finally { + _channelLock.unlock(); } } public void asyncRpc(Method m, CompletableFuture future) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { ensureIsOpen(); quiescingAsyncRpc(m, future); + } finally { + _channelLock.unlock(); } } public void quiescingAsyncRpc(Method m, CompletableFuture future) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { enqueueAsyncRpc(m, future); quiescingTransmit(m); + } finally { + _channelLock.unlock(); } } @@ -402,13 +429,16 @@ public void processShutdownSignal(ShutdownSignalException signal, boolean ignoreClosed, boolean notifyRpc) { try { - synchronized (_channelMutex) { + _channelLock.lock(); + try { if (!setShutdownCauseIfOpen(signal)) { if (!ignoreClosed) throw new AlreadyClosedException(getCloseReason()); } - _channelMutex.notifyAll(); + _channelLockCondition.signalAll(); + } finally { + _channelLock.unlock(); } } finally { if (notifyRpc) @@ -424,30 +454,40 @@ public void notifyOutstandingRpc(ShutdownSignalException signal) { } public void transmit(Method m) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { transmit(new AMQCommand(m)); + } finally { + _channelLock.unlock(); } } public void transmit(AMQCommand c) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { ensureIsOpen(); quiescingTransmit(c); + } finally { + _channelLock.unlock(); } } public void quiescingTransmit(Method m) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { quiescingTransmit(new AMQCommand(m)); + } finally { + _channelLock.unlock(); } } public void quiescingTransmit(AMQCommand c) throws IOException { - synchronized (_channelMutex) { + _channelLock.lock(); + try { if (c.getMethod().hasContent()) { while (_blockContent) { try { - _channelMutex.wait(); + _channelLockCondition.await(); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } @@ -460,6 +500,8 @@ public void quiescingTransmit(AMQCommand c) throws IOException { } this._trafficListener.write(c); c.transmit(this); + } finally { + _channelLock.unlock(); } } diff --git a/src/main/java/com/rabbitmq/client/impl/AMQCommand.java b/src/main/java/com/rabbitmq/client/impl/AMQCommand.java index a761e5cce6..8ee99c9701 100644 --- a/src/main/java/com/rabbitmq/client/impl/AMQCommand.java +++ b/src/main/java/com/rabbitmq/client/impl/AMQCommand.java @@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.concurrent.locks.ReentrantLock; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Command; @@ -43,6 +44,7 @@ public class AMQCommand implements Command { /** The assembler for this command - synchronised on - contains all the state */ private final CommandAssembler assembler; + private final ReentrantLock assemblerLock = new ReentrantLock(); AMQCommand(int maxBodyLength) { this(null, null, null, maxBodyLength); @@ -115,7 +117,8 @@ public void transmit(AMQChannel channel) throws IOException { int channelNumber = channel.getChannelNumber(); AMQConnection connection = channel.getConnection(); - synchronized (assembler) { + assemblerLock.lock(); + try { Method m = this.assembler.getMethod(); if (m.hasContent()) { byte[] body = this.assembler.getContentBody(); @@ -145,6 +148,8 @@ public void transmit(AMQChannel channel) throws IOException { } else { connection.writeFrame(m.toFrame(channelNumber)); } + } finally { + assemblerLock.unlock(); } connection.flush(); @@ -155,7 +160,8 @@ public void transmit(AMQChannel channel) throws IOException { } public String toString(boolean suppressBody){ - synchronized (assembler) { + assemblerLock.lock(); + try { return new StringBuilder() .append('{') .append(this.assembler.getMethod()) @@ -165,6 +171,8 @@ public String toString(boolean suppressBody){ .append(contentBodyStringBuilder( this.assembler.getContentBody(), suppressBody)) .append('}').toString(); + } finally { + assemblerLock.unlock(); } } diff --git a/src/main/java/com/rabbitmq/client/impl/ChannelN.java b/src/main/java/com/rabbitmq/client/impl/ChannelN.java index d08593eaa8..9a3ee7cc83 100644 --- a/src/main/java/com/rabbitmq/client/impl/ChannelN.java +++ b/src/main/java/com/rabbitmq/client/impl/ChannelN.java @@ -361,10 +361,13 @@ private void releaseChannel() { return true; } else if (method instanceof Channel.Flow) { Channel.Flow channelFlow = (Channel.Flow) method; - synchronized (_channelMutex) { + _channelLock.lock(); + try { _blockContent = !channelFlow.getActive(); transmit(new Channel.FlowOk(!_blockContent)); - _channelMutex.notifyAll(); + _channelLockCondition.signalAll(); + } finally { + _channelLock.unlock(); } return true; } else if (method instanceof Basic.Ack) { @@ -524,7 +527,8 @@ private void asyncShutdown(Command command) throws IOException { false, command.getMethod(), this); - synchronized (_channelMutex) { + _channelLock.lock(); + try { try { processShutdownSignal(signal, true, false); quiescingTransmit(new Channel.CloseOk()); @@ -533,6 +537,9 @@ private void asyncShutdown(Command command) throws IOException { notifyOutstandingRpc(signal); } } + finally { + _channelLock.unlock(); + } notifyListeners(); } @@ -612,9 +619,12 @@ public AMQCommand transformReply(AMQCommand command) { try { // Synchronize the block below to avoid race conditions in case // connection wants to send Connection-CloseOK - synchronized (_channelMutex) { + _channelLock.lock(); + try { startProcessShutdownSignal(signal, !initiatedByApplication, true); quiescingRpc(reason, k); + } finally { + _channelLock.unlock(); } // Now that we're in quiescing state, channel.close was sent and @@ -1602,16 +1612,22 @@ public CompletableFuture asyncCompletableRpc(Method method) throws IOEx @Override public void enqueueRpc(RpcContinuation k) { - synchronized (_channelMutex) { + _channelLock.lock(); + try { super.enqueueRpc(k); dispatcher.setUnlimited(true); + } finally { + _channelLock.unlock(); } } @Override protected void markRpcFinished() { - synchronized (_channelMutex) { + _channelLock.lock(); + try { dispatcher.setUnlimited(false); + } finally { + _channelLock.unlock(); } } diff --git a/src/main/java/com/rabbitmq/client/impl/SocketFrameHandler.java b/src/main/java/com/rabbitmq/client/impl/SocketFrameHandler.java index 5048100a7b..607f4c4112 100644 --- a/src/main/java/com/rabbitmq/client/impl/SocketFrameHandler.java +++ b/src/main/java/com/rabbitmq/client/impl/SocketFrameHandler.java @@ -29,6 +29,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** * A socket-based frame handler. @@ -48,9 +49,11 @@ public class SocketFrameHandler implements FrameHandler { /** Socket's inputstream - data from the broker - synchronized on */ private final DataInputStream _inputStream; + private final ReentrantLock _inputStreamLock = new ReentrantLock(); /** Socket's outputstream - data to the broker - synchronized on */ private final DataOutputStream _outputStream; + private final ReentrantLock _outputStreamLock = new ReentrantLock(); private final int maxInboundMessageBodySize; @@ -127,7 +130,8 @@ public int getTimeout() * @see #sendHeader() */ public void sendHeader(int major, int minor) throws IOException { - synchronized (_outputStream) { + _outputStreamLock.lock(); + try { _outputStream.write("AMQP".getBytes("US-ASCII")); _outputStream.write(1); _outputStream.write(1); @@ -139,6 +143,8 @@ public void sendHeader(int major, int minor) throws IOException { LOGGER.error("TLS connection failed: {}", e.getMessage()); throw e; } + } finally { + _outputStreamLock.unlock(); } } @@ -154,7 +160,8 @@ public void sendHeader(int major, int minor) throws IOException { * @see #sendHeader() */ public void sendHeader(int major, int minor, int revision) throws IOException { - synchronized (_outputStream) { + _outputStreamLock.lock(); + try { _outputStream.write("AMQP".getBytes("US-ASCII")); _outputStream.write(0); _outputStream.write(major); @@ -166,6 +173,8 @@ public void sendHeader(int major, int minor, int revision) throws IOException { LOGGER.error("TLS connection failed: {}", e.getMessage()); throw e; } + } finally { + _outputStreamLock.unlock(); } } @@ -184,15 +193,21 @@ public void initialize(AMQConnection connection) { @Override public Frame readFrame() throws IOException { - synchronized (_inputStream) { + _inputStreamLock.lock(); + try { return Frame.readFrom(_inputStream, this.maxInboundMessageBodySize); + } finally { + _inputStreamLock.unlock(); } } @Override public void writeFrame(Frame frame) throws IOException { - synchronized (_outputStream) { + _outputStreamLock.lock(); + try { frame.writeTo(_outputStream); + } finally { + _outputStreamLock.unlock(); } }